const gRDFRoot = "rdf://tabmix";
const HSitems = 3; // in history we have title, url, scrollpos
var NC_TM = [];
var gSessionPath = ["", "", "", ""];
var nsIPrefServiceObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
var SessionPref = nsIPrefServiceObj.getBranch("extensions.tabmix.sessions.");
var gThisWin = null;
var gThisWinTabs = null;
var gThisWinClosedtabs = null;

var tmSHEntry     = Components.interfaces.nsISHEntry;
var tmSHistory    = Components.interfaces.nsISHistory;
var tmRDFResource = Components.interfaces.nsIRDFResource;

const BUTTON_OK = 0;
const BUTTON_CANCEL = 1;
const BUTTON_EXTRA1 = 2;
const SHOW_MENULIST = 1;
const SHOW_TEXTBOX = 0;
const HIDE_MENUANDTEXT = 2;
const CHECKBOX_UNCHECKED = 0;
const CHECKBOX_CHECKED = 1;
const HIDE_CHECKBOX = 2;
const SELECT_DEFAULT = 0;
const SELECT_LASTSESSION = 1;
const SELECT_CRASH = 2;
const SHOW_CLOSED_WINDOW_LIST = 3;
const DLG_SAVE = 0;
const DLG_RENAME = 1;
const NO_NEED_TO_REPLACE = -1;

function tmLog(aMessage) {
   var tmConsoleService = Components.classes['@mozilla.org/consoleservice;1']
      .getService(Components.interfaces.nsIConsoleService);
   tmConsoleService.logStringMessage('TabMix: \n' + aMessage);
}

function object2String(_obj) {
tmLog("=============== object to string ===============");
   var _objS = _obj.toString() + ":\n";
   for (var item in _obj)
      try {
         _objS += item + " =  " + _obj[item] + "\n";
      } catch (er) { _objS += item + " =  " + "error in this item" + "\n";}
   tmLog(_objS);
   return _objS;
}

/*
  sanitize privte data by delete the files session.rdf session.old
*/
var TMP_Sanitizer = {
   addSanitizeItem: function ()
   {
      window.removeEventListener('load', TMP_Sanitizer.addSanitizeItem, true);
      if (typeof Sanitizer != 'function')
         return;
      // Sanitizer will execute this
      Sanitizer.prototype.items['extensions-tabmix'] = {
         clear : function() {
            try {
               TMP_Sanitizer.sanitize();
            } catch (ex) {
               try { Components.utils.reportError(ex); } catch(ex) {}
            }
         },
         get canClear() {
            return true;
         }
      }
   },

   addMenuItem: function ()
   {
      var prefs = document.getElementsByTagName('preferences')[0];
      var firstCheckbox = document.getElementsByTagName('checkbox')[0];
      if (prefs && firstCheckbox) // if this isn't true we are lost :)
      {
         var pref = document.createElement('preference');
         pref.setAttribute('id', 'privacy.item.extensions-tabmix');
         pref.setAttribute('name', 'privacy.item.extensions-tabmix');
         pref.setAttribute('type', 'bool');
         prefs.appendChild(pref);

         var check = document.createElement('checkbox');
         check.setAttribute('label', tabmixSanitize.label);
         check.setAttribute('accesskey', tabmixSanitize.accesskey);
         check.setAttribute('preference', 'privacy.item.extensions-tabmix');
         firstCheckbox.parentNode.insertBefore(check, firstCheckbox);

         if (typeof(gSanitizePromptDialog) == 'object')
         {
            pref.setAttribute('readonly', 'true');
            check.setAttribute('onsyncfrompreference', 'return gSanitizePromptDialog.onReadGeneric();');
         }
      }
   },

   tryToSanitize: function ()
   {
      var prefService = Components.classes["@mozilla.org/preferences-service;1"]
                        .getService(Components.interfaces.nsIPrefBranch);

      try {
         var promptOnSanitize = prefService.getBoolPref("privacy.sanitize.promptOnSanitize");
      } catch (e) { promptOnSanitize = true;}

      // if promptOnSanitize is true we call TMP_Sanitizer.sanitize from Firefox Sanitizer
      if (promptOnSanitize)
         return false;

      try {
         var sanitizeTabmix = prefService.getBoolPref("privacy.item.extensions-tabmix");
      } catch (e) { sanitizeTabmix = false;}

      if (!sanitizeTabmix)
         return false;

      this.sanitize();
      return true;
   },

// XXX need to add test if we fail to delete then alert the user or ....?
   sanitize: function ()
   {
      // get file references
      var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
                           .getService(Components.interfaces.nsIProperties);
      var sessionFile = dirService.get("ProfD", Components.interfaces.nsILocalFile);
      var sessionFileBackup = sessionFile.clone();
      sessionFile.append("session.rdf");
      sessionFileBackup.append("session.old");

      // remove the files from the disk
      this.clearDisk(sessionFile);
      this.clearDisk(sessionFileBackup);

      // init new DATASource for all open window
      var enumerator = windowEnumerator();
      var wnd, _sessionManager, broadcaster, btn;
      while ( enumerator.hasMoreElements() ) {
         wnd = enumerator.getNext();

         // clear DATASource
         delete wnd.gBrowser.windowID;
         _sessionManager = wnd.SessionManager;
         _sessionManager.corruptedFile = false;
         _sessionManager.RDFService.UnregisterDataSource(_sessionManager.DATASource);
         // init new DATASource
         _sessionManager.initDATASource();

         // disable closed window list button
         broadcaster = wnd.document.getElementById("tmp_closedwindows");
         if (broadcaster)
            broadcaster.setAttribute("disabled",true);

         // clear closed tabs and disable the button
         wnd.gBrowser.closedTabs = [];
         btn = wnd.document.getElementById("btn_undoclose");
         if (btn)
            btn.setAttribute("disabled", true);
      }
   },

   clearDisk: function (file)
   {
      if (file.exists()) {
         try {
            file.remove(false);
         }
         catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
      }
   }

}


var SessionData = {
   docShellItems: ["allowImages","allowSubframes","allowMetaRedirects","allowPlugins","allowJavascript"],
   tabAttribute:  ["protected","locked"],

   getTabProperties: function(aTab, checkPref) {
      if (typeof(checkPref) == "undefined") checkPref = false; // pref check is only for session manager
      var tabProperties = "", temp;
      for ( var j = 0; j < this.tabAttribute.length; j++ ){
         temp = aTab.hasAttribute(this.tabAttribute[j]) ? aTab.getAttribute(this.tabAttribute[j]) : "false";
         tabProperties += (temp=="true") ? "1" : "0";
      }
      // if save.permissions is false we save all Permissions as on, so if we change this pref after session
      // was saved, the session will load with Permissions as on.
      if (checkPref && !SessionPref.getBoolPref("save.permissions")) return tabProperties + "11111";
      var aTabDocShell = gBrowser.getBrowserForTab(aTab).docShell;
      for ( j = 0; j < this.docShellItems.length; j++ ){
         tabProperties += aTabDocShell[this.docShellItems[j]] ? "1" : "0";
      }
      return tabProperties;
   },

   setTabProperties: function(aTab, tabProperties, checkPref) {
      if (typeof(checkPref) == "undefined") checkPref = false; // pref check is only for session manager
      var aPermission;
      var k = this.tabAttribute.length;
      for ( var j = 0; j < k; j++ ){
         //extensions.tabmix.sessions.save.protected && extensions.tabmix.sessions.save.locked
         if (!checkPref || SessionPref.getBoolPref("save." + this.tabAttribute[j])) {
            if (tabProperties.charAt(j) == "1") aTab.setAttribute(this.tabAttribute[j], "true");
            else if (aTab.hasAttribute(this.tabAttribute[j])) aTab.removeAttribute(this.tabAttribute[j]);
         }
      }
      if (checkPref && !SessionPref.getBoolPref("save.permissions")) return;
      var aTabDocShell = gBrowser.getBrowserForTab(aTab).docShell;
      for ( j = 0; j < this.docShellItems.length; j++ ) {
         aPermission = tabProperties.charAt(j + k) == "1";
         if (aTabDocShell[this.docShellItems[j]] != aPermission)
            aTabDocShell[this.docShellItems[j]] = aPermission;
      }
   }
}

function windowEnumerator() {
  var WindowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
                        .getService(Components.interfaces.nsIWindowMediator);
  return WindowManager.getEnumerator("navigator:browser");
}

function numberOfWindows(all) {
  var enumerator = windowEnumerator();
  var count = 0;
  while ( enumerator.hasMoreElements() ) {
    count++;
    if (!all && count == 2)
      break;
    enumerator.getNext();
  }
  return count;
}

var SessionManager = {
    RDFService: null,
    CONUtils: null,
    DATASource: null,
    IOService: null,
    overwriteWindow: false,
    saveThisWindow: true,
    NC_NS : "http://home.netscape.com/NC-rdf#",
    enableBackup: null,
    enableManager: null,
    enableSaveHistory: null,
    saveClosedtabs: null,
    corruptedFile: false,

    // call by TM_init
   init: function SM_init() {
      this.enableManager = SessionPref.getBoolPref("manager");
      this.enableBackup = SessionPref.getBoolPref("crashRecovery");
      this.enableSaveHistory = SessionPref.getBoolPref("save.history");
      this.saveClosedtabs = SessionPref.getBoolPref("save.closedtabs") &&
                             tabxPrefs.getBoolPref("undoClose");

      if (!this.DATASource) this.initService();
      var isFirstWindow = numberOfWindows() == 1 && !window.duplicat;
      var path, status, caller, crashed;
      if (isFirstWindow) {
         var sessionstore = gIsFirefox2 && gPref.getBoolPref("browser.sessionstore.enabled");
         path = gRDFRoot + "/closedSession/thisSession";
         status = this.getLiteralValue(path, "status");
         crashed = status.indexOf("crash") != -1;
         // if this isn't delete on exit, we know next time that firefox crash
         SessionPref.setBoolPref("crashed" , true); // we use this in setup.js;
         nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
         this.setLiteral(path, "status", "crash");

         // check if we after restart
         var afterRestart = false;
         if (tabxPrefs.prefHasUserValue("resume_session_once")) {
            afterRestart = tabxPrefs.getBoolPref("resume_session_once");
            tabxPrefs.clearUserPref("resume_session_once");
         }

         if (!this.enableManager && (!this.enableBackup || !crashed)) {
            // we not after crash and session manager not enabled ...
            // we delete previous closed session
            this.removeAllClosedSession();
            this.dataFlush();
            return;
         }

         if (crashed)
            this.openAfterCrash(status);
         else if (this.enableManager)
            this.openFirstWindow(false, afterRestart);

         if (tabxPrefs.prefHasUserValue("warnAboutClosingTabs.timeout"))
            tabxPrefs.clearUserPref("warnAboutClosingTabs.timeout")

      } else if (this.enableManager && window.name.indexOf("tabmix") != -1) {
         path = window.name.split("-")[1];
         caller = window.name.split("-")[2];

         if (caller == "concatenatewindows")
            this.loadSession(path, caller, false);
         else
            this.loadOneWindow(path, "windowopenebytabmix");
      }

      if (this.enableManager)
         this.updateClosedWindowsMenu("check");

      this.dataFlush();
   },

   // call by TM_deinit
   deinit: function SM_deinit(isLastWindow, askBeforSave, startTime) {
      // When Exist Firefox:
      //       pref "extensions.tabmix.sessions.onClose"
      //       0 - Save
      //       1 - Ask me Befor Save
      //       2 (or else) - Don't Save
      // we check this when last window is about to close for all other window the session is saved
      // in closed window list.
      // in the last window if the user pref in not to save we delete the closed window list.

      if (this.windowClosed)
         return;
      this.windowClosed = true;

      // check if we need to sanitize on exit without prompt to user
      try {
         if (!gIsOldBrowser && isLastWindow && gPref.getBoolPref("privacy.sanitize.sanitizeOnShutdown")) {
            // if tryToSanitize is false and privacy.sanitize.promptOnSanitize is true
            // we call TMP_Sanitizer.sanitize from Firefox Sanitizer
            if (TMP_Sanitizer.tryToSanitize())
               return;
         }
      } catch (err) {}

      var closedWinList = document.getElementById("tmp_closedwindows").getAttribute("disabled");
      if (this.enableManager && this.saveThisWindow) {
         for (var i = 0; i < gBrowser.mTabs.length; i++)
            gBrowser.mTabs[i].removeAttribute("inrestore");
         this.saveOneWindow(gSessionPath[0], "windowclosed");
      }
      if (isLastWindow) {
         if ( this.enableManager ) {
            var result = {}, saveClosedTabs;
            result.button = SessionPref.getIntPref("onClose");

            // firefox closed by restart, always save the session
            if (!askBeforSave) {
               tabxPrefs.setBoolPref("resume_session_once", true);
               result.button = BUTTON_OK;
            }

            result.checked = saveClosedTabs = this.saveClosedtabs;
            var pref = "extensions.tabmix.warnAboutClosingTabs.timeout";
            var delay = gPref.prefHasUserValue(pref) ? gPref.getCharPref(pref)*1 : 0;
            var curTime = !startTime ? new Date().valueOf() - delay : startTime - delay;
            if (result.button == 1) { // Ask me Befor Save
               document.getElementById("tmp_closedwindows").setAttribute("disabled", closedWinList);
               window.focus();
               var bundle_session = document.getElementById("bundle_session_manager");
               var title = bundle_session.getString("sm.askBeforSave.title");
               var msg = bundle_session.getString("sm.askBeforSave.msg0") + "\n\n"
                        + bundle_session.getString("sm.askBeforSave.msg1");
               var chkBoxLabel = bundle_session.getString("sm.saveClosedTab.chkbox.label");
               var chkBoxState = saveClosedTabs ? CHECKBOX_CHECKED : HIDE_CHECKBOX;
               var buttons = [this.setLabel("sm.askBeforSave.button0"),
                               this.setLabel("sm.askBeforSave.button1")].join("\n");
               result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, chkBoxState],
                              [title, msg, "", chkBoxLabel, buttons]);
            }
            if (result.button == BUTTON_OK) {
               var rdfNodeClosedWindows = this.RDFService.GetResource(gSessionPath[0]);
               var sessionContainer = this.initContainer(rdfNodeClosedWindows);
               this.checkTime(sessionContainer, curTime);
               this.deleteWithProp(sessionContainer, "dontLoad");
               // count windows and tabs in this session
               var count = this.countWinsAndTabs(sessionContainer);
               if (count.win > 0 && count.tab > 0) {
                  this.setLiteral(rdfNodeClosedWindows, "nameExt", this.getNameData(count.win, count.tab));
                  // delete closed tab list for this session
                  if (saveClosedTabs && !result.checked) this.deleteAllClosedtabs(sessionContainer);
               } else this.deleteSubtree(gSessionPath[0]);
            } else {
               // delete the closed window list.
               this.deleteSubtree(gSessionPath[0]);
            }
         }
         if (this.enableBackup) this.deleteSession(gSessionPath[3]);
         if (tabxPrefs.prefHasUserValue("warnAboutClosingTabs.timeout"))
            tabxPrefs.clearUserPref("warnAboutClosingTabs.timeout");
         if (SessionPref.prefHasUserValue("crashed"))
            SessionPref.clearUserPref("crashed"); // we use this in setup.js;
         nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
         this.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "");
         if (!this.enableManager && !this.enableBackup)
            this.deleteSession(gSessionPath[0]);
         this.dataFlush();
      }
   },

   // XXX split this for each pref that has change
   // XXX need to update after permissions, locked, locked
   updateSettings: function() {
      // list of session manager pref
      //          sessions.manager - ok
      //          sessions.crashRecovery - ok
      //          sessions.save.closedtabs - ok
      //          sessions.save.history - ok
      //          sessions.save.permissions - ok (update evry time this function run because lock is change)
      //          sessions.save.locked - ok (update evry time this function run because lock is change)
      //          sessions.save.protected - ok (update evry time this function run because lock is change)
      //          sessions.save.selectedtab - ok
      // xxx      sessions.save.scrollposition - ok (update with history) // xxx need to divide it
      //          undoClose -
      //          undoCloseCache -
      //
      var sessionManager = tabxPrefs.getBoolPref("sessions.manager");
      var crashRecovery = tabxPrefs.getBoolPref("sessions.crashRecovery");
      var enableClosedtabs = tabxPrefs.getBoolPref("sessions.save.closedtabs");
      var enableSaveHistory = tabxPrefs.getBoolPref("sessions.save.history");
      var undoClose = tabxPrefs.getBoolPref("undoClose");
      var cache = tabxPrefs.getIntPref("undoCloseCache");

       // hide or show session manager buttons & menus
      document.getElementById("tm-sessionmanager").hidden = !sessionManager || !tabxPrefs.getBoolPref("sessionToolsMenu");
      var hiddenPref = !sessionManager || !tabxPrefs.getBoolPref("closedWinToolsMenu");
      document.getElementById("tm-sm-closedwindows").hidden = hiddenPref;
      document.getElementById("tm-sm-closedwindows2").hidden = !hiddenPref;

      var button = document.getElementById("btn_sessionmanager");
      if (button)
         button.setAttribute("disabled",!sessionManager);

      // we dont need this function to run befor sessionmanager init
      // this.enableBackup=null so we dont past the next if
      if (!this.DATASource) return;
      var windowSaved = false, closedTabSaved = false;
      if (this.enableBackup != crashRecovery) {
         if (crashRecovery) { // save all open window and tab
            this.saveAllWindows(gSessionPath[0], "windowbackup");
            windowSaved = true;
         } else { // remove all backup
            this.deleteSession(gSessionPath[0], "status", "backup");
            this.deleteSession(gSessionPath[3]);
            this.initSession(gSessionPath[0], gThisWin);
            this.dataFlush();
         }
         this.enableBackup = crashRecovery;
      }
      var winPath = gThisWin;
      if (crashRecovery)
         this.tabSelected(); // this is fast so we dont check if the pref is changed ( just for now)
      if (this.enableManager != sessionManager) {
         this.enableManager = sessionManager;
      }
      if (undoClose) {
         while ( gBrowser.closedTabs.length > cache ) getClosedTab('delete', 0); // delete extra closedTabs from closedtabs list and session.RDF
      } else if (gBrowser.closedTabs.length > 0) delayRestoreTab("original", -1); // flush closedtabs list and session.RDF
      if (this.saveClosedtabs != enableClosedtabs && undoClose) {
         this.saveClosedtabs = enableClosedtabs && undoClose;
         if (enableClosedtabs) {
            // save if gBrowser.closedTabs.length > 0 and we save backup and save closedtab backup
            if (crashRecovery && gBrowser.closedTabs.length > 0 && undoClose) {
               this.initSession(gSessionPath[0], winPath);
               this.copyClosedTabsToRDF(winPath);
            }
            closedTabSaved = true;
         } else {
         // if undoClose = false we delete all above
            if (undoClose) this.deleteWinClosedtabs(winPath); // flush only closedTabs list in session.RDF
         }
      }
      if (this.enableSaveHistory != enableSaveHistory) {
         this.enableSaveHistory = enableSaveHistory;
         if (crashRecovery) {
            if (!windowSaved) this.saveAllTab(winPath, 0);
            if (!closedTabSaved && enableClosedtabs && gBrowser.closedTabs.length > 0 && undoClose) {
               this.initSession(gSessionPath[0], winPath);
               this.deleteWinClosedtabs(winPath);
               this.copyClosedTabsToRDF(winPath);
               closedTabSaved = true;
            }
         }
      }
      if (closedTabSaved) {
         this.initSession(gSessionPath[0], gThisWin);
         this.dataFlush();
      }
   },

   loadHomePage: function() {
      var homePage = gHomeButton.getHomePage();
      if (homePage == window.arguments[0]) {
         this.setStripVisibility(homePage.split("|").length);
         BrowserHome();
         if (!gBrowser.isBlankBrowser(gBrowser.mCurrentBrowser))
            content.focus();
      }
      else if (gBrowser.mCurrentTab.loadOnStartup)
         for (var i = 0; i < gBrowser.mTabs.length ; i++)
            delete gBrowser.mTabs[i].loadOnStartup;
      else
         gBrowser.mCurrentBrowser.reload();
   },

    // init common services
   initService: function() {
      this.RDFService = Components.classes["@mozilla.org/rdf/rdf-service;1"]
                       .getService(Components.interfaces.nsIRDFService);
      this.CONUtils = Components.classes["@mozilla.org/rdf/container-utils;1"]
                           .getService(Components.interfaces.nsIRDFContainerUtils);
      this.IOService = Components.classes["@mozilla.org/network/io-service;1"]
                              .getService(Components.interfaces.nsIIOService);
      this.setNC_TM();
      this.initDATASource();
   },

   initDATASource: function() {
      var file = this.profileDir;
      file.append("session.rdf");
      var uri = this.IOService.newFileURI(file).spec;
      try {
         this.DATASource = this.RDFService.GetDataSourceBlocking(uri);
      } catch (e) { // corrupted session.rdf
         var bundle_session = document.getElementById("bundle_session_manager");
         var title = bundle_session.getString("sm.corrupted.title");
         var msg = bundle_session.getString("sm.corrupted.msg0") + "\n"
                  + bundle_session.getString("sm.corrupted.msg1");
         var buttons = ["", this.setLabel("sm.button.continue")].join("\n");
         TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],[title, msg, "", "", buttons]);
         tmLog(e);
         file.moveTo(this.profileDir, "session.old");
         this.DATASource = this.RDFService.GetDataSourceBlocking(uri);
         this.corruptedFile = true;
      }

      // set path to session type
      var path = gRDFRoot + "/closedSession/";
      var sessionType = ["thisSession", "lastSession", "previoustolastSession", "crashedsession"];
      var closedSession = this.initContainer(path);
      var i, aEntry;
      if (closedSession.GetCount()==0) { // create the list
         for (i = 0; i < sessionType.length; i++) {
            aEntry = this.RDFService.GetResource(path + sessionType[i]);
            this.setResource(aEntry, "session", gRDFRoot + "/closed" + i + "/window");
            closedSession.AppendElement(aEntry);
         }
      }
      for (i = 0; i < sessionType.length; i++) {
         gSessionPath[i] = this.getResourceValue(path + sessionType[i], "session");
      }
      if (typeof(gBrowser) == "object" && !gBrowser.windowID) {
         gBrowser.windowID = this.RDFService.GetAnonymousResource().Value.split("rdf:")[1];
         gThisWin = gSessionPath[0] + "/" + gBrowser.windowID;
         gThisWinTabs = gThisWin + "/tabs";
         gThisWinClosedtabs = gThisWin + "/closedtabs";
      }
   },

   get profileDir() {
      return Components.classes["@mozilla.org/file/directory_service;1"]
            .getService(Components.interfaces.nsIProperties)
            .get("ProfD", Components.interfaces.nsILocalFile);
   },

   setNC_TM: function() {
      var rdfLabels = ["tabs","closedtabs","index","history","properties","selectedIndex",
               "timestamp","title","url","dontLoad","reOpened","name","nameExt","session",
               "status","tabPos", "indent","image","scroll","winFeatures"];
      for (var i = 0; i < rdfLabels.length; i++) {
         NC_TM[rdfLabels[i]] = this.RDFService.GetResource(this.NC_NS + rdfLabels[i]);
      }
   },

   setLabel: function(property, bundleID) {
      var strimgBundle = bundleID ? document.getElementById(bundleID) :
            document.getElementById("bundle_session_manager");
      var label = strimgBundle.getString(property + ".label");
      var key = strimgBundle.getString(property + ".accesskey");
      var accessKeyIndex = label.toLowerCase().indexOf(key.toLowerCase());
      if (accessKeyIndex > -1)
         label = label.substr(0, accessKeyIndex) + "&" + label.substr(accessKeyIndex);
      return label;
   },

   deleteNode: function(rdfNode) {
      var arcOut = this.DATASource.ArcLabelsOut(rdfNode);
      while (arcOut.hasMoreElements()) {
         var aLabel = arcOut.getNext();
         if (aLabel instanceof tmRDFResource) {
            var aTarget = this.DATASource.GetTarget(rdfNode, aLabel, true);
            this.DATASource.Unassert(rdfNode, aLabel, aTarget);
         }
      }
   },

   deleteSubtree: function(labelRoot) {
      var allElements = this.DATASource.GetAllResources();
      while (allElements.hasMoreElements()) {
         var aResource = allElements.getNext();
         if ((aResource instanceof tmRDFResource) && (aResource.Value.indexOf(labelRoot) == 0))
            this.deleteNode(aResource);
      }
   },

   initContainer: function(node) {
var pNode = node;
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
try{
      return this.CONUtils.MakeSeq(this.DATASource, node);
} catch (e) {
   tmLog("Error in initContainer" + "\n" + "node = " + pNode + "\n\n" + "caller " + this.initContainer.caller.toString().split(";")[0]);
   return "error"
}
   },

    // return true if node is empty container or node is not container
   containerEmpty: function(node) {
var pNode = node;
try{
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
      if (!this.CONUtils.IsContainer(this.DATASource, node)) return true;
      return this.CONUtils.IsEmpty(this.DATASource, node);
} catch (e) {
   tmLog("Error in containerEmpty" + "\n" + "node = " + pNode + "\n\n" + "caller " + this.containerEmpty.caller.toString().split(";")[0]);
   return "error"
}
   },

   wrapContainer: function(path, prop) {
      var root = this.getResource(path, prop);
      var container = this.initContainer(root);
if (container == "error") { tmLog("wrapContainer error path " + path + "\n" + "prop " + prop); return "error"}
      return {
         Root: root,
         Container: container,
         Enum: container.GetElements(),
         Count: container.GetCount()
      }
   },

   getValue: function(node, label, typeID, def) {
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
      label = NC_TM[label];
      var rdfNode = this.DATASource.GetTarget(node, label, true);
      return (rdfNode instanceof Components.interfaces[typeID]) ? rdfNode.Value : def;
   },

   getLiteralValue: function(node, arc, def) {
      if (typeof(def) == "undefined") def = "";
      return this.getValue(node, arc, "nsIRDFLiteral", def);
   },

   getIntValue: function(node, arc, def) {
      if (typeof(def) == "undefined") def = 0;
      return this.getValue(node, arc, "nsIRDFInt", def);
   },

   getResourceValue: function(node, arc, def) {
      if (typeof(def) == "undefined") def = null;
      return this.getValue(node, arc, "nsIRDFResource", def);
   },

   getResource: function(node, arc) {
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
      arc = NC_TM[arc];
      return this.DATASource.GetTarget(node, arc, true);
   },

   nodeHasArc: function(node, arc) {
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
      arc = NC_TM[arc];
      return this.DATASource.hasArcOut(node, arc);
   },

   setLiteral: function SM_setLiteral(node, arc, value) {
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
      arc = NC_TM[arc];
      value = this.RDFService.GetLiteral(value);
      this.changeValue(node, arc, value);
   },

   setIntLiteral: function(node, arc, value) {
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
      arc = NC_TM[arc];
      value = this.RDFService.GetIntLiteral(value);
      this.changeValue(node, arc, value);
   },

   setResource: function(node, arc, value) {
      if (typeof(node) == "string") node = this.RDFService.GetResource(node);
      arc = NC_TM[arc];
      if (typeof(value) == "string") value = this.RDFService.GetResource(value);
      this.changeValue(node, arc, value);
   },

   changeValue: function(node, arc, newValue) {
      if (this.DATASource.hasArcOut(node, arc)) {
         var oldValue = this.DATASource.GetTarget(node, arc, true);
         if (newValue != oldValue) this.DATASource.Change(node, arc, oldValue, newValue);
      } else this.DATASource.Assert(node, arc, newValue, true);
   },

   dataFlush: function() {
      this.DATASource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
   },

   promptReplaceStartup: function(caller, path) {
      var loadsession = SessionPref.getIntPref("onStart.loadsession");
      var sessionpath = SessionPref.getCharPref("onStart.sessionpath");
      var result = {button: NO_NEED_TO_REPLACE};
      if (loadsession < 0 || sessionpath != path) return result;
      var label = this.getLiteralValue(path, "name");
      var selectionFlag = SELECT_DEFAULT;
      var title, msg, buttons;
      var bundle_session = document.getElementById("bundle_session_manager");
      var areYouSure = bundle_session.getString("sm.areYouSure.msg");
      var chooseStartup = bundle_session.getString("sm.canChooseStartup.msg");
      switch ( caller ) {
         case "addWinToSession":
            title = bundle_session.getString("sm.addtoStartup.title");
            var msgType = caller=="addWinToSession" ? "windows" : "tabs";
            msg = bundle_session.getString("sm.addtoStartup.msg." + msgType) + "\n" + label
               +  "\n" + areYouSure + "\n\n" + chooseStartup;
            buttons = [this.setLabel("sm.addtoStartup.button0"),
                       this.setLabel("sm.addtoStartup.button1")].join("\n");
            break;
         case "replaceSession":
            title = bundle_session.getString("sm.replaceStartup.title");
            msg = bundle_session.getString("sm.replaceStartup.msg") + "\n" + label
               +  "\n" + areYouSure + "\n\n" + chooseStartup;
            buttons = [this.setLabel("sm.replaceStartup.button0"),
                       this.setLabel("sm.replaceStartup.button1")].join("\n");
            break;
         case "removeSavedSession":
            title = bundle_session.getString("sm.removeStartup.title");
            msg = bundle_session.getString("sm.removeStartup.msg0") + "\n" + label
               +  "\n" + areYouSure + "\n\n" + bundle_session.getString("sm.removeStartup.msg1");
            buttons = [this.setLabel("sm.removeStartup.button0"),
                       this.setLabel("sm.removeStartup.button1")].join("\n");
            selectionFlag = SELECT_LASTSESSION;
            break;
      }
      return TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, selectionFlag],
                  [title, msg, "", "", buttons]);
   },

   addWinToSession: function SM_addWinToSession(action) {
      if (!this.isValidtoSave()) return;
      var path = document.popupNode.session;
      var result = this.promptReplaceStartup("addWinToSession", path);
      if (result.button == BUTTON_CANCEL) return;
      else if (result.button == BUTTON_OK) this.replaceStartupPref(result, "");
      var saveClosedTabs = this.saveClosedtabs;
      var rdfNodeSession = this.RDFService.GetResource(path);
      var sessionContainer = this.initContainer(rdfNodeSession);
      var oldCount = this.countWinsAndTabs(sessionContainer);
      var newCount = this.saveOneOrAll(action, path, saveClosedTabs);
      if (newCount) {
         var numTabs = oldCount.tab + newCount.tab;
         var numWindows = oldCount.win + newCount.win;
         this.setLiteral(rdfNodeSession, "nameExt", this.getNameData(numWindows, numTabs));
         this.dataFlush();
      }
   },

   saveClosedSession: function() {
      var oldPath = document.popupNode.session;
      var id = this.RDFService.GetAnonymousResource().Value.split("rdf:")[1];
      var path = gRDFRoot + "/saved/" + id + "/window";
      var pathToReplace = "";
      var session = this.getSessionName("saveprevious", this.getLiteralValue(oldPath, "name"));
      if (session.button == BUTTON_CANCEL) return; // user cancel
      else if (session.button == BUTTON_EXTRA1) { // we replace exist session, BUTTON_OK - save new session
         var result = this.promptReplaceStartup("replaceSession", session.path);
         if (result.button == BUTTON_CANCEL) return; // user cancel
         else if (result.button == BUTTON_OK) { // we replace startup session
            this.replaceStartupPref(result, path);
         }
         pathToReplace = session.path;
      }
      container = this.initContainer(path)
      var pathNode, container, extID = "";
      var node = document.popupNode.parentNode.parentNode;
      if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows")
         extID = "/" + id;
      this.copySubtree(oldPath, path + extID);
      if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
         node = this.RDFService.GetResource(path + extID);
         container.InsertElementAt(node, 1, true);
         this.DATASource.Unassert(node, NC_TM["dontLoad"], this.RDFService.GetLiteral("true"));
      }
      var count = this.countWinsAndTabs(container); // we need it just to fix the date
      if (!session.saveClosedTabs)
         this.deleteAllClosedtabs(container);
      if (count)
         this.insertSession(count, session.name, path, pathToReplace);
      else
         tmLog("Error in saveClosedSession");
   },

   copyNode: function(oldNode, newNode, oldRoot, newRoot) {
      var newTarget;
      var arcOut = this.DATASource.ArcLabelsOut(oldNode);
      while (arcOut.hasMoreElements()) {
         var aLabel = arcOut.getNext();
         if (aLabel instanceof tmRDFResource) {
            var aTarget = this.DATASource.GetTarget(oldNode, aLabel, true);
            if (aTarget instanceof tmRDFResource) {
               newTarget = aTarget.Value.replace(oldRoot, newRoot);
               aTarget = this.RDFService.GetResource(newTarget);
            }
            this.DATASource.Assert(newNode, aLabel, aTarget, true);
         }
      }
   },

   copySubtree: function (oldRoot, newRoot) {
      var allElements = this.DATASource.GetAllResources();
      while (allElements.hasMoreElements()) {
         var aResource = allElements.getNext();
         if ((aResource instanceof tmRDFResource) && (aResource.Value.indexOf(oldRoot) == 0)) {
            var newNodeLabel = aResource.Value.replace(oldRoot, newRoot);
            this.copyNode(aResource, this.RDFService.GetResource(newNodeLabel), oldRoot, newRoot);
         }
      }
   },

   replaceStartupPref: function(result, newPath) {
      var sessionpath = !newPath ? "--" : SessionPref.getCharPref("onStart.sessionpath");
      SessionPref.setIntPref("onStart.loadsession", result.value);
      if (result.value > -1) {
         if (result.label == sessionpath ) SessionPref.setCharPref("onStart.sessionpath", newPath);
         else SessionPref.setCharPref("onStart.sessionpath", result.label);
      }
      nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
   },

   sessionUtil: function(action, what) {
      // action = save , replace
      // type = thiswindow , allwindows
      if (!this.isValidtoSave()) return;
      if (numberOfWindows() == 1) what = "thiswindow";
      var oldPath = "", name, saveClosedTabs;
      var id = this.RDFService.GetAnonymousResource().Value.split("rdf:")[1];
      var newPath = gRDFRoot + "/saved/" + id + "/window";
      if (action == "save") {
         // ask the user for new name or for exist name if the user want to replace
         var session = this.getSessionName("save"+what);
         if (session.button == BUTTON_CANCEL) return; // user cancel
         else if (session.button == BUTTON_EXTRA1) oldPath = session.path;
         name = session.name;
         saveClosedTabs = session.saveClosedTabs;
      } else {
         oldPath = document.popupNode.session;
         name = this.getLiteralValue(oldPath, "name");
         saveClosedTabs = this.saveClosedtabs;
      }
      if (oldPath != "") { // oldPath is "" if we save to a new name
         // check if the user want to replace startup session
         var result = this.promptReplaceStartup("replaceSession", oldPath);
         if (result.button == BUTTON_CANCEL) return; // user cancel
         else if (result.button == BUTTON_OK) this.replaceStartupPref(result, newPath);
      }
      var count = this.saveOneOrAll("save"+what, newPath, saveClosedTabs);
      if (count) this.insertSession(count, name, newPath, oldPath);
      else tmLog("Error in " + action + " " + what);
   },

   isValidtoSave: function() {
      if ( !this.enableManager ) return false;
      var bundle_session = document.getElementById("bundle_session_manager");
      if (gBrowser.isBlankWindow()) {
         var title = bundle_session.getString("sm.title");
         var msg = bundle_session.getString("sm.dontSaveBlank.msg");
         var buttons = ["", this.setLabel("sm.button.continue")].join("\n");
         TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],[title, msg, "", "", buttons]);
         return false;
      }
      return true;
   },

   saveOneOrAll: function(action, path, saveClosedTabs) {
      var numTabs, numWindows;
      switch ( action ) {
         case "savethiswindow":
               numTabs = this.saveOneWindow(path, "", false, saveClosedTabs);
               numWindows = 1;
            break;
         case "saveallwindows":
               var didSaved = this.saveAllWindows(path, "", saveClosedTabs);
               numTabs = didSaved.tab;
               numWindows = didSaved.win;
            break;
         default: return false;
      }
      if (numTabs > 0) return {win: numWindows, tab: numTabs};
      bundle_session = document.getElementById("bundle_session_manager");
      alert(bundle_session.getString("sm.sessoinSave.error"));
      return false;
   },

   insertSession: function SM_insertSession(count, name, path, oldPath) {
      var container = this.initContainer(gRDFRoot + "/windows");
      var index = 0;
      if (oldPath != "") index = container.IndexOf(this.RDFService.GetResource(oldPath));
      var node = this.RDFService.GetResource(path);
      container.InsertElementAt(node, index+1, true);
      if (oldPath != "") { // remove the session we replace
         container.RemoveElementAt(index, true);
         this.removeSession(oldPath, gRDFRoot+'/windows');
      }
      this.setLiteral(node, "name", name);
      this.setLiteral(node, "nameExt", this.getNameData(count.win, count.tab));
      this.dataFlush();
      return true;
   },

   getSessionName: function(action, old) {
      var bundle_session = document.getElementById("bundle_session_manager");
      var showChebox, closedtabMsg, saveClosedTabs = this.saveClosedtabs;
      if (action != "rename" && saveClosedTabs) {
         closedtabMsg = bundle_session.getString("sm.saveClosedTab.chkbox.label");
         showChebox = CHECKBOX_CHECKED;
      } else showChebox = HIDE_CHECKBOX;
      var msg = bundle_session.getString("sm.sessionName.msg0") + "\n";
      var title = bundle_session.getString("sm.sessionName.title." + action);
      var label, buttons, actionFlag;
      var sessionList = this.getSessionList("saved");
      if (action=="rename") {
         label = old;
         buttons = [this.setLabel("sm.sessionName.button0"),
                        this.setLabel("sm.sessionName.button1")].join("\n");
         actionFlag = DLG_RENAME;
      } else {
         label = action == "saveprevious" ? old : gBrowser.mCurrentTab.label;
         buttons = [this.setLabel("sm.askBeforSave.button0"),
                        this.setLabel("sm.askBeforSave.button1"),
                        this.setLabel("sm.replaceStartup.button0")+"..."].join("\n");
         actionFlag = DLG_SAVE;
         for (var i = 0; i < sessionList.list.length; i++) {
            if (label == sessionList.list[i]) {
               label = "";
               break;
            }
         }
      }
      label += "\n" + sessionList.list.join("\n");
      var result = TM_PromptService([BUTTON_OK, SHOW_TEXTBOX, showChebox, actionFlag],[title, msg, label, closedtabMsg, buttons]);
      switch (result.button) {
         case BUTTON_CANCEL: return {button: result.button};
         case BUTTON_OK:
         case BUTTON_EXTRA1 :
            var trimResult = result.label.replace(/^[\s]+/g,"").replace(/[\s]+$/g,"");
            return {button: result.button, name: trimResult, path: sessionList.path[result.value], saveClosedTabs: result.checked};
      }
      return {};
   },

   countWinsAndTabs: function(container) {
      // count windows and tabs in this session
      var numTabs = 0, numWindows = 0;
      var windowEnum = container.GetElements();
      while (windowEnum.hasMoreElements()) {
         var rdfNodeWindow = windowEnum.getNext();
         numWindows += 1;
         var rdfNodeTabs = this.getResource(rdfNodeWindow, "tabs");
         if (rdfNodeTabs instanceof tmRDFResource) {
            var tabContainer = this.initContainer(rdfNodeTabs);
            numTabs += tabContainer.GetCount();
         }
      }
      return {win: numWindows, tab: numTabs};
   },

   getNameData: function(numWindows, numTabs) {
      var d = new Date();
      var date = [d.getFullYear(), '/', d.getMonth()<9 ? "0":"", d.getMonth()+1, '/', d.getDate()<10 ? "0":"", d.getDate()].join('');
      var time = [d.getHours()<10 ? "0":"", d.getHours(), ':', d.getMinutes()<10 ? "0":"", d.getMinutes(), ':', d.getSeconds()<10 ? "0":"", d.getSeconds()].join('');
      var bundle_session = document.getElementById("bundle_session_manager");
      var empty = bundle_session.getString("sm.session.empty");
      var T = bundle_session.getString("sm.session.tabs");
      var W = bundle_session.getString("sm.session.windows");
      if (numWindows == 0) return ", (" + empty + ") (" + date + " " + time + ")";
      else if (numWindows < 2) return ", (" + numTabs + " "+ T + ") (" + date + " " + time + ")";
      return ", (" + numWindows + " " + W + ", " + numTabs + " " + T + ") (" + date + " " + time + ")";
   },

   updateSessionMenu: function(menu) {
      var overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows") || gSingleWindowMode;
      document.getElementById("tm-sm-OpenInCurrenWindow").setAttribute("default",overwriteWindows);
      document.getElementById("tm-sm-OpenInNewWindow").setAttribute("default",!overwriteWindows);
      document.getElementById("tm-sm-OpenInNewWindow").hidden = gSingleWindowMode;

      var mValue = document.popupNode.getAttribute("value");
      if (mValue <= -1)
         document.getElementById("tm-sm-Rename").setAttribute("disabled",true);
      else
         document.getElementById("tm-sm-Rename").removeAttribute("disabled");

      var node = document.popupNode.parentNode.parentNode;
      var mItem = document.getElementById("tm-sm-SetAsStartup");
      if (node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
         mItem.removeAttribute("disabled");
         if (document.popupNode.hasAttribute("default"))
            mItem.setAttribute("checked", "true");
        else
            mItem.removeAttribute("checked");
      } else {
         mItem.removeAttribute("checked");
         mItem.setAttribute("disabled",true);
      }

      var mShowext = document.getElementById("tm-sm-showext");
      var showext = SessionPref.getBoolPref("menu.showext");
      if (!showext && mShowext.hasAttribute("checked"))
         mShowext.removeAttribute("checked");
      else if (showext && !mShowext.hasAttribute("checked"))
         mShowext.setAttribute("checked", "true");

      var obsAll = document.getElementById("tmp_contextmenu_AllWindows");
      var obsThis = document.getElementById("tmp_contextmenu_ThisWindow");
      var mSave = document.getElementById("tm-sm-Save");
      if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows" || mValue <= -1) {
         if (obsAll.hidden != true)
            obsAll.hidden = true;
         if (obsThis.hidden != true)
            obsThis.hidden = true;
         if (mSave.hidden != false)
            mSave.hidden = false;
         if (document.popupNode.hasAttribute("disabled"))
            mSave.setAttribute("disabled", true);
         else
            mSave.removeAttribute("disabled");
      } else {
         var isOneWindow = (numberOfWindows() == 1);
         if (obsAll.hidden != isOneWindow)
            obsAll.hidden = isOneWindow;
         if (obsThis.hidden != false)
            obsThis.hidden = false;
         if (mSave.hidden != true)
            mSave.hidden = true;
      }
      return typeof(document.popupNode.session) != "undefined";
   },

   restoreSession: function(node, overwriteWindows) {
      // call restoreSession after delay to let the popup menu time to hide
      window.setTimeout( function () { SessionManager.delayRestoreSession(node, overwriteWindows); }, 0 );
   },

   delayRestoreSession: function(node, overwriteWindows) {
      var path = node.session;
      var id = node.parentNode.parentNode.id;
      if (id == "tm-sessionmanager" || id == "btn_sessionmanager")
         this.loadSession(path,'sessionrestore', overwriteWindows);
      else if (id.indexOf("tm-sm-closedwindows")==0 || id == "btn_closedwindows")
         this.openclosedwindow(path, overwriteWindows);
   },

   setSessionAsStartup: function(popup) {
      if (popup.getAttribute("checked")) {
         var aValue = document.popupNode.getAttribute("value"); // -1, -2 for for closed session, 1,2.... for saved session
         var loadsession = aValue && aValue <= -1 ? aValue : 0;
         SessionPref.setIntPref("onStart.loadsession", loadsession);
         if (loadsession > -1)
            SessionPref.setCharPref("onStart.sessionpath", document.popupNode.session);
         nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
      }
   },

   setShowNameExt: function() {
      SessionPref.setBoolPref("menu.showext", !SessionPref.getBoolPref("menu.showext"));
      nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
   },

   renameSession: function SM_renameSession() {
      var thisSession = document.popupNode.session;
      var node = this.RDFService.GetResource(thisSession);
      var oldName = this.getLiteralValue(node, "name");
      var result = this.getSessionName("rename", oldName);
      if (result.button == BUTTON_OK) {
         this.setLiteral(node, "name", result.name);
         this.dataFlush();
      }
   },

   removeFromMenu: function(event, popup, root) {
      if (!tabxPrefs.getBoolPref("middleclickDelete")) return;
      if ( event.button == 1 && ("session" in event.target)) {
         document.popupNode = event.target;
         SessionManager.removeSavedSession();
         if (root == gSessionPath[0] && this.isClosedWindowsEmpty()) popup.hidePopup();
         else SessionManager.createMenu(popup, root);
      }
   },

   removeSavedSession: function() {
      var node = document.popupNode.parentNode.parentNode;
      var path = document.popupNode.session;
      if (node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
         // befor we remove this session check if it is the startup session
         // and let the user cancel the delete or choose diffrent startup session
         var result = this.promptReplaceStartup("removeSavedSession", path);
         switch (result.button) {
            case BUTTON_CANCEL: return;
            case BUTTON_OK: this.replaceStartupPref(result, "");
            case NO_NEED_TO_REPLACE : this.removeSession(path, gRDFRoot+'/windows');
         }
      } else if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
         this.removeSession(path, gSessionPath[0]);
         this.updateClosedWindowsMenu("check");
      }
   },

   removeAllSavedSession: function() {
      var node = document.popupNode.parentNode.parentNode;
      var result, title, msg;
      var bundle_session = document.getElementById("bundle_session_manager");
      var buttons = [this.setLabel("sm.removeStartup.button0"),
                     this.setLabel("sm.removeStartup.button1")].join("\n");
      if (node.id == "tm-sessionmanager" || node.id == "btn_sessionmanager") {
         title = bundle_session.getString("sm.removeAll.title.session");
         msg = bundle_session.getString("sm.removeAll.msg0") + "\n\n";
         if (SessionPref.getIntPref("onStart.loadsession") > -1)
            msg += bundle_session.getString("sm.removeAll.msg1");
         result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],
                        [title, msg, "", "", buttons]);
         if (result.button == BUTTON_OK) {
            this.deleteSubtree(gSessionPath[1]);
            this.deleteSubtree(gSessionPath[2]);
            this.deleteSubtree(gSessionPath[3]);
            this.deleteSubtree(gRDFRoot+'/saved');
            this.deleteSubtree(gRDFRoot+'/windows');
            this.dataFlush();
            SessionPref.setIntPref("onStart.loadsession", -1);
            nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
         }
      } else if (node.id.indexOf("tm-sm-closedwindows")==0 || node.id == "btn_closedwindows") {
         title = bundle_session.getString("sm.removeAll.title.closedwindow");
         msg = bundle_session.getString("sm.removeAll.msg2");
         result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, HIDE_CHECKBOX],
                        [title, msg, "", "", buttons]);
         if (result.button == BUTTON_OK) {
            var sessionContainer = this.initContainer(gSessionPath[0]);
            this.deleteWithProp(sessionContainer, "status", "saved");
            this.updateClosedWindowsMenu(true);
            this.dataFlush();
         }
      }
   },

   // xxx need to check if we need all this functions
   removeSession: function(value, container) {
      if (value==null) return;
      var node = this.RDFService.GetResource(value);
      var rdfNodeWindows = this.RDFService.GetResource(container);
      var windowsContainer = this.initContainer(rdfNodeWindows);
      this.deleteSubtree(value);
      windowsContainer.RemoveElement(node, true);
      if (!windowsContainer.GetCount()) this.deleteNode(rdfNodeWindows);
      this.dataFlush();
   },

   removeAllClosedSession: function() {
      for (var i = 0; i < gSessionPath.length; i++) {
         var rdfNode = this.RDFService.GetResource(gSessionPath[i]);
         var container = this.initContainer(rdfNode);
         if (!this.containerEmpty(gSessionPath[i])) this.deleteWithProp(container);
         if (!container.GetCount()) this.deleteNode(rdfNode);
      }
   },

   deleteSession: function(nodLabel, prop, value) {
      var rdfNode = this.RDFService.GetResource(nodLabel);
      var container = this.initContainer(rdfNode);
      if (!this.containerEmpty(nodLabel)) this.deleteWithProp(container, prop, value);
      if (!container.GetCount()) this.deleteNode(rdfNode);
   },

   deleteWithProp: function(container, prop, value) {
      var containerEnum = container.GetElements();
      var nodeToDelete = [];
      var noProp = typeof(prop) == "undefined";
      var valueExist = typeof(value) == "string";
      while(containerEnum.hasMoreElements()) {
         var node = containerEnum.getNext();
         var propExist = noProp ? true : this.nodeHasArc(node, prop);
         if (valueExist && !noProp && propExist && this.getLiteralValue(node, prop) != value) propExist = false;
         if (propExist) nodeToDelete.push(node);
      }
      this.deleteArrayNodes(container, nodeToDelete, true);
   },

   deleteArrayNodes: function(container, nodeToDelete, deleteSubTree) {
      for (var i = 0; i < nodeToDelete.length; i++) {
         var nodeValue = nodeToDelete[i].QueryInterface(tmRDFResource).Value
         if (deleteSubTree) this.deleteSubtree(nodeValue);
         container.RemoveElement(nodeToDelete[i], true);
      }
   },

   destroyMenuItems: function(menu) {
      // Destroy the items.
      var destroy = false, endSeparator;
      for (var i = 0; i < menu.childNodes.length; i++) {
         var item = menu.childNodes[i];
         if (item.id.indexOf("-endSeparator") != -1) {
            endSeparator = item
            if (menu.parentNode.id != "tm-sessionmanager" &&
               menu.parentNode.id != "btn_sessionmanager") break;
            else continue;
         }
         if (destroy) {
            i--;
            menu.removeChild(item);
         } else if (item.id.indexOf("-startSeparator") != -1) destroy = true;
      }
      return endSeparator;
   },

   createMenuForDialog: function(popup, contents) {
      if (contents == SHOW_CLOSED_WINDOW_LIST) {
         // create closed window list popup menu
         this.createMenu(popup, window.opener.gSessionPath[0], contents);
      } else {
         // create saved Session popup menu
         this.createMenu(popup, gRDFRoot+'/windows', contents);
         // check if sessionpath and loadsessions valid for saved session
         var loadsession = SessionPref.getIntPref("onStart.loadsession");
         if (loadsession > -1 && contents != 1 && loadsession != popup.parentNode.sessionIndex) {
            SessionPref.setIntPref("onStart.loadsession", popup.parentNode.sessionIndex);
            var pref = "onStart.sessionpath";
            if (popup.parentNode.sessionIndex < 0 && SessionPref.prefHasUserValue(pref))
               SessionPref.clearUserPref(pref);
         }
      }
   },

   createMenu: function(popup, container, contents) {
      if (!this.DATASource) this.initService(); // initService if we call from pref dialog
      if (typeof(contents) == "undefined") contents = 0;
      var endSeparator = this.destroyMenuItems(popup); // Remove any existing menu items
      var parentId = popup.parentNode.id
      if (parentId == "btn_sessionmanager" || parentId == "btn_closedwindows")
         popup.parentNode.removeAttribute("tooltiptext");
      var parentID = (contents != SHOW_CLOSED_WINDOW_LIST) ? popup.parentNode.id : "";
      var aContainer = this.initContainer(container);
      var containerEnum = aContainer.GetElements();
      var mi, i = -1, node, name, nameExt, accessKey;
      var showNameExt = SessionPref.getBoolPref("menu.showext");
      var loadsession = SessionPref.getIntPref("onStart.loadsession");
      var sessionpath = SessionPref.getCharPref("onStart.sessionpath");
      var showTooltip = parentId == "btn_sessionmanager" || parentId == "tm-sessionmanager" || parentId.indexOf("tm-sm-closedwindows")==0 || parentId == "btn_closedwindows";
      while(containerEnum.hasMoreElements()) {
         node = containerEnum.getNext();
         if (this.nodeHasArc(node, "status") &&
               this.getLiteralValue(node, "status") != "saved") continue;
         name = this.getLiteralValue(node, "name");
         nameExt = this.getLiteralValue(node, "nameExt");
         // Insert a menu item for session in the container
         mi = document.createElement("menuitem");
         mi.session = node.QueryInterface(tmRDFResource).Value;
         mi.setAttribute("session", mi.session);
         if (contents == 1 && loadsession > -1 && mi.session && mi.session == sessionpath) continue;
         ++i;
         mi.setAttribute("value", i);
         mi.value = i;
         if (parentID != "onStart.loadsession") {
            accessKey = (i > 26) ? "" : String.fromCharCode(65+i) + "  " ;
            mi.setAttribute("accesskey", accessKey);
            mi.setAttribute("label", accessKey + name + (showNameExt ? nameExt : ""));
            if (showTooltip) mi.setAttribute("tooltiptext", accessKey + name + nameExt);
         } else {
            mi.setAttribute("label", name);
         }
         popup.insertBefore(mi, endSeparator);
      }
      switch ( parentID ) {
         case "tm-sessionmanager":
         case "btn_sessionmanager":
            var observer = document.getElementById("tmp_menu_AllWindows");
            var isOneWindow = (numberOfWindows() == 1);
            if (observer.hidden != isOneWindow) observer.hidden = isOneWindow;
         case "tm_prompt":
            endSeparator.hidden = endSeparator.previousSibling.localName == "menuseparator";
            var sessionLabel;
            var afterCrash = !this.containerEmpty(gSessionPath[3]);
            // if Crashed is empty don't show 'Crashed Session' menu item
            var bundle_session = document.getElementById("bundle_session_manager");
            if (afterCrash && contents != 1)
               sessionLabel = [bundle_session.getString("sm.sessionMenu.lastgood"),
                               bundle_session.getString("sm.sessionMenu.previous"),
                               bundle_session.getString("sm.sessionMenu.crashed")];
            else
               sessionLabel = [bundle_session.getString("sm.sessionMenu.last"),
                               bundle_session.getString("sm.sessionMenu.previous")];
            var menu;
            var empty = ", (" + bundle_session.getString("sm.session.empty") + ")";
            for (i = 0; i < sessionLabel.length; i++ ){
               menu = document.createElement("menuitem");
               menu.session = gSessionPath[i+1];
               if (this.containerEmpty(menu.session) && contents != 1) menu.setAttribute("disabled", "true");
               nameExt = this.getLiteralValue(menu.session, "nameExt", empty);
               menu.setAttribute("label", sessionLabel[i] + (showNameExt && contents != 1 ? nameExt : ""));
               if (showTooltip) menu.setAttribute("tooltiptext", sessionLabel[i] + nameExt);
               menu.setAttribute("value", (-1 - i));
               popup.appendChild (menu);
            }
            if (afterCrash && contents != 1) { // add separator befor Crashed menu item
               menu = document.createElement("menuseparator");
               popup.insertBefore(menu, popup.lastChild);
            }
            if (contents == 1) loadsession = -1; //set "Last Sessoin" as default in the list
            this.setDefaultIndex(popup, loadsession, sessionpath);
            break;
         case "onStart.loadsession":
            endSeparator.hidden = this.containerEmpty(container);
            this.setDefaultIndex(popup, loadsession, sessionpath);
            break;
         default:
            endSeparator.hidden = true;
            break;
      }
   },

   // set defaultIndex, sessionIndex and default Attribute
   setDefaultIndex : function(popup, loadsession, sessionpath) {
      popup.parentNode.defaultIndex = -1; // index with menuseparator
      popup.parentNode.sessionIndex = -1; // index without menuseparator
      var i, item, value, checked;
      for (i = 0; i < popup.childNodes.length; i++) {
         item = popup.childNodes[i];
         if (item.localName == "menuseparator") continue;
         value = item.getAttribute("value");
         checked = ((loadsession > -1 && item.session && item.session == sessionpath) ||
               (loadsession <= -1 && value && value == loadsession));
         if (checked) {
            item.setAttribute("default", "true");
            popup.parentNode.defaultIndex = i;
            popup.parentNode.sessionIndex = value;
         } else item.removeAttribute("default");
      }
   },

   // update disable/enable to closed window list in tool menu and toolbar
   updateClosedWindowsMenu: function(action) {
      var disabled = (action == "check") ? this.isClosedWindowsEmpty(): action;
      var wnd, enumerator = windowEnumerator();
      while ( enumerator.hasMoreElements() ) {
         wnd = enumerator.getNext();
         var broadcaster = wnd.document.getElementById("tmp_closedwindows");
         if (broadcaster)
            broadcaster.setAttribute("disabled",disabled);
      }
   },

   isClosedWindowsEmpty: function() {
      if ( !this.enableManager )
         return true;
      var node, disabled = true;
      var aContainer = this.initContainer(gSessionPath[0]);
      var containerEnum = aContainer.GetElements();
      while(containerEnum.hasMoreElements()) {
         node = containerEnum.getNext();
         if (this.getLiteralValue(node, "status") == "saved") {
            disabled = false;
            break;
         }
      }
      return disabled;
   },

   // call by init on first window load after crash
   openAfterCrash: function SM_openAfterCrash(status) {
      var sessionContainer = this.initContainer(gSessionPath[0]);
      if (this.enableBackup) {
         var path = gRDFRoot + "/closedSession/thisSession";
         this.setLiteral(path, "status", "crash2");
         // restore to were we was befor the crash
         var crashedContainer = this.initContainer(gSessionPath[3]);
         if (status != "crash2") {
            // delete old crash data
            if (!this.containerEmpty(gSessionPath[3])) this.deleteWithProp(crashedContainer);
            var windowEnum = sessionContainer.GetElements();
            var nodeToDelete = [];
            while (windowEnum.hasMoreElements()) {
               var rdfNodeWindow = windowEnum.getNext();
               if (this.getLiteralValue(rdfNodeWindow, "status") == "backup") {
                  var tabs = this.getResource(rdfNodeWindow, "tabs");
                  var subTree = rdfNodeWindow.QueryInterface(tmRDFResource).Value;
                  if (!this.containerEmpty(tabs)) { // copy "backup" subtree to crashed session if it not empty
                     var newSubTree = subTree.replace(gSessionPath[0], gSessionPath[3]);
                     this.copySubtree(subTree, newSubTree);
                     crashedContainer.AppendElement(this.RDFService.GetResource(newSubTree));
                  }
                  this.deleteSubtree(subTree); // delete the crashed subtree
                  nodeToDelete.push(rdfNodeWindow);// remove the window from the crash session
               } else this.setLiteral(rdfNodeWindow, "dontLoad", "true"); // we can see this session in the close window list
            }
            this.deleteArrayNodes(sessionContainer, nodeToDelete, false);
         } // if firefox was crashed in middle of crash Recovery try again to restore the same data
         else if (!this.containerEmpty(gSessionPath[0]))
            this.deleteWithProp(sessionContainer);

         var bundle_session = document.getElementById("bundle_session_manager");
         var title = bundle_session.getString("sm.afterCrash.title");
         var msg;
         if (status != "crash2")
            msg = bundle_session.getString("sm.afterCrash.msg0");
         else
            msg = bundle_session.getString("sm.afterCrash.msg0.again");
         var chkBoxLabel = !this.enableManager ? bundle_session.getString("sm.afterCrash.chkbox.label") : "";
         var buttons, result, whattoLoad = "session";
         var chkBoxState = !this.enableManager ? CHECKBOX_UNCHECKED : HIDE_CHECKBOX;
         var closedWinList = this.initContainer(gSessionPath[0]).GetCount();
         var lastSession = this.containerEmpty(gSessionPath[1]) // last session
         var prevtoLast = this.containerEmpty(gSessionPath[2]) // previous to last
         var savedSession = this.containerEmpty(gRDFRoot+'/windows') // saved session
         var isAllEmpty = lastSession && prevtoLast && savedSession;
         if (!this.containerEmpty(gSessionPath[3])) { // if Crashed Session is not empty
            var count = this.countWinsAndTabs(crashedContainer);
            this.setLiteral(gSessionPath[3], "nameExt", this.getNameData(count.win, count.tab));
            if (this.enableManager && !isAllEmpty) {
               msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg1");
               buttons = [this.setLabel("sm.afterCrash.button0"),
                          this.setLabel("sm.afterCrash.button1")].join("\n");
               result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, SELECT_CRASH],
                              [title, msg, "", "", buttons]);
            } else {
               msg += " " + bundle_session.getString("sm.afterCrash.msg2") + ".....";
               if (!this.enableManager)
                  msg += "\n" + bundle_session.getString("sm.afterCrash.msg3");
               else
                  msg += "\n" + bundle_session.getString("sm.afterCrash.msg4");
               buttons = [this.setLabel("sm.afterCrash.button0.crashed"),
                          this.setLabel("sm.afterCrash.button1")].join("\n");
               result = TM_PromptService([BUTTON_OK, HIDE_MENUANDTEXT, chkBoxState],
                              [title, msg, "", chkBoxLabel, buttons]);
               result.label = gSessionPath[3];
            }
         } else {
            if (this.enableManager && !isAllEmpty) {
               msg += " " + bundle_session.getString("sm.afterCrash.msg5") + "\n\n"
                          + bundle_session.getString("sm.afterCrash.msg1");
               buttons = [this.setLabel("sm.afterCrash.button0"),
                          this.setLabel("sm.afterCrash.button1")].join("\n");
               result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, HIDE_CHECKBOX, SELECT_DEFAULT],
                              [title, msg, "", "", buttons]);
            } else if (closedWinList != 0) {
               msg += " " + bundle_session.getString("sm.afterCrash.msg6");
               if (!this.enableManager)
                  msg += "\n" + bundle_session.getString("sm.afterCrash.msg3") + "\n\n"
                              + bundle_session.getString("sm.afterCrash.msg7") + ":";
               else
                  msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg7") + " "
                                + bundle_session.getString("sm.afterCrash.msg8") + ":";
               buttons = [this.setLabel("sm.afterCrash.button0"),
                          this.setLabel("sm.afterCrash.button1")].join("\n");
               result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, chkBoxState, SHOW_CLOSED_WINDOW_LIST],
                              [title, msg, "", chkBoxLabel, buttons]);
               whattoLoad = "closedwindow";
            } else {// nothing to restore
               msg = bundle_session.getString("sm.afterCrash.msg9") + "\n" + bundle_session.getString("sm.afterCrash.msg10");
               if (!this.enableManager)
                  msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg3");
               buttons = ["", this.setLabel("sm.button.continue")].join("\n");
               result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, chkBoxState],
                              [title, msg, "", chkBoxLabel, buttons]);
            }
         }
         if (result.checked && !this.enableManager) {
            SessionPref.setBoolPref("manager", true); // enable session manager
            try {
               nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
            } catch(ex) { }
         }
         if (result.button == BUTTON_OK) {
            switch ( whattoLoad ) {
               case "session": this.loadSession(result.label, "firstwindowopen");
                  break;
               case "closedwindow": this.openclosedwindow(result.label, true);
                  break;
               default:
            }
         } else
            this.loadHomePage();
      } else { // crash recovery is off, delete any remains from the crashed session
         if (!this.containerEmpty(gSessionPath[0])) this.deleteWithProp(sessionContainer);
         if (this.enableManager) this.openFirstWindow(true, false); // openFirstWindow with flag openAfterCrash
         //  else BrowserHome(); // we never get to here...
      }
   },

   // call by init or by openAfterCrash on first window load
   openFirstWindow: function SM_openFirstWindow(afterCrash, afterRestart) {
      var path = gRDFRoot + "/closedSession/";
      var sessionType = ["thisSession", "lastSession", "previoustolastSession", "crashedsession"];
      // swap 0 --> 1 --> 2 --> 0
      var i;
      var sessions = [], subTree, aSession;
      for (i = 0; i < sessionType.length-1; i++) {
         sessions.push(this.getResource(path + sessionType[i], "session"));
      }
      for (i = 0; i < sessionType.length-1; i++) {
         if (i == 0) { // delete oldest session subtree
            aSession = sessions[sessionType.length-2];
            subTree = aSession.QueryInterface(tmRDFResource).Value;
            this.deleteSubtree(subTree);
         } else aSession = sessions[i-1];
         this.setResource(path + sessionType[i], "session", aSession)
      }
      for (i = 0; i < sessionType.length; i++) {
         gSessionPath[i] = this.getResourceValue(path + sessionType[i], "session");
      }
      gThisWin = gSessionPath[0] + "/" + gBrowser.windowID;
      gThisWinTabs = gThisWin + "/tabs";
      gThisWinClosedtabs = gThisWin + "/closedtabs";
      // When Firefox Starts:
      //       pref "onStart"
      //       0 - Restore
      //       1 - Ask me Befor Restore
      //       2 (or else) - Don't Restore
      //
      //       pref "onStart.loadsession"
      //       0 , 1 , 2 ..... index of saved sessions
      //       -1, -2 ........ index of previous sessions
      //
      // if loadsession >= 0 the session path is saved in pref "onStart.sessionpath"
      // else if loadsession < 0 the session path is saved in gSessionPath
      var restoreFlag = SessionPref.getIntPref("onStart");
      if (restoreFlag > 1 && !afterRestart) {
         return; // Don't Restore
      }
      var loadSession = SessionPref.getIntPref("onStart.loadsession");
      // after last session end with restart load the last session without any prompt
      // unless we are after crash
      var askifempty = restoreFlag > 1 ? false : SessionPref.getBoolPref("onStart.askifempty");
      var startupEmpty = false, savePref = false ;
      var result = {}, title, msg = "", buttons;
      var bundle_session = document.getElementById("bundle_session_manager");
      if (afterCrash)
         title = bundle_session.getString("sm.afterCrash.title");
      else
         title = bundle_session.getString("sm.start.title");
      var chkBoxLabel = afterCrash ? bundle_session.getString("sm.start.chkbox.label") : "";
      var chkBoxState = afterCrash ? CHECKBOX_UNCHECKED : HIDE_CHECKBOX;
      // get saved session list
      var sessionList = this.getSessionList();
      if (sessionList == null) {
         if ((askifempty || restoreFlag == 1) && !this.corruptedFile) {
            msg = bundle_session.getString("sm.start.msg0") + "\n"
                + bundle_session.getString("sm.afterCrash.msg10");
            if (afterCrash)
               msg += "\n\n" + bundle_session.getString("sm.start.msg1");
            buttons = ["", this.setLabel("sm.button.continue")].join("\n");
            result = TM_PromptService([BUTTON_CANCEL, HIDE_MENUANDTEXT, chkBoxState],
                           [title, msg, "", chkBoxLabel, buttons]);
            if (result.checked && afterCrash) {
               SessionPref.setBoolPref("crashRecovery", true); // enable Crash Recovery
               nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
            }
         }
         this.loadHomePage();
         return;
      }
      if (afterRestart) {
         restoreFlag = 0;
         loadSession = -1;
      }
      var aList = sessionList.list;
      var sessionPath = sessionList.path;
      var loadSessionIsValid = true, sessionIndex, thisPath;
      switch ( (loadSession > 0) ? 0 : loadSession ) {
         case 0:
            sessionIndex = null;
            if (SessionPref.prefHasUserValue("onStart.sessionpath")) {
               thisPath = SessionPref.getCharPref("onStart.sessionpath");
               // check if sessionpath is valid
               for (i = 0; i < aList.length; i++) {
                  if (sessionPath[i] == thisPath) {
                     sessionIndex = i;
                     break;
                  }
               }
            }
            if ((thisPath && this.containerEmpty(thisPath)) || sessionIndex == null) {
               // error in pref.js or in session.rdf ask the user what to do
               loadSessionIsValid = false;
               thisPath = gSessionPath[1]; // load last session
               SessionPref.setIntPref("onStart.loadsession", -1);
               savePref = true;
            }
            break;
         default: // just in case that somehow onStart.loadsession is invalid
            loadSession = -1;
            SessionPref.setIntPref("onStart.loadsession", -1);
            savePref = true;
         case -2:
         case -1:
            var indx = -1 * loadSession;
            thisPath = gSessionPath[indx];
            if (this.containerEmpty(gSessionPath[indx])) startupEmpty = true;
            sessionIndex = aList.length + indx - 3;
            break;
      }
      if (restoreFlag > 0 || afterCrash || (startupEmpty && askifempty) || !loadSessionIsValid) {
try{
         if (afterCrash)
            msg += bundle_session.getString("sm.afterCrash.msg0") + " "
                 + bundle_session.getString("sm.start.msg1");
         if (startupEmpty) msg += bundle_session.getString("sm.start.msg0");
         if (!loadSessionIsValid) msg += bundle_session.getString("sm.start.msg2");
         msg += "\n\n" + bundle_session.getString("sm.afterCrash.msg1");
         buttons = [this.setLabel("sm.afterCrash.button0"),
                    this.setLabel("sm.afterCrash.button1")].join("\n");
         result = TM_PromptService([BUTTON_OK, SHOW_MENULIST, chkBoxState, SELECT_DEFAULT],
                        [title, msg, "", chkBoxLabel, buttons]);
         if (result.checked && afterCrash) {
            SessionPref.setBoolPref("crashRecovery", true); // enable Crash Recovery
            savePref = true;
         }
} catch (e) {tmLog("error " + e);}
      }
      else {
         result.button = startupEmpty ? BUTTON_CANCEL : BUTTON_OK;
         result.label = thisPath;
      }
      if (savePref) nsIPrefServiceObj.savePrefFile(null); // store the pref immediately
      if (result.button == BUTTON_OK)
         this.loadSession(result.label, "firstwindowopen");
      else
         this.loadHomePage();
   },

   getSessionList: function(flag) {
      var aList = [], sessionPath = [];
      var aContainer = this.initContainer(gRDFRoot+'/windows');
      var containerEnum = aContainer.GetElements();
      var node, aName;
      while(containerEnum.hasMoreElements()) {
         node = containerEnum.getNext();
         aName = this.getLiteralValue(node, "name");
         aList.push(aName);
         sessionPath.push(node.QueryInterface(tmRDFResource).Value);
      }
      var bundle_session = document.getElementById("bundle_session_manager");
      if (flag == "saved") return {list: aList, path: sessionPath};
      else if (flag == "replace") {
         aList.push(bundle_session.getString("sm.sessionMenu.lastDefault"));
         aList.push(bundle_session.getString("sm.sessionMenu.previous"));
      } else {
         var empty = ", (" + bundle_session.getString("sm.session.empty") + ")";
         var empty1 = this.containerEmpty(gSessionPath[1]);
         var empty2 = this.containerEmpty(gSessionPath[2]);
         if (empty1 && empty2 && aList.length == 0) return null;
         if (flag == "afterCrash") aList.push(bundle_session.getString("sm.sessionMenu.lastgood") + (empty1 ? empty : ""));
         else aList.push(bundle_session.getString("sm.sessionMenu.last") + (empty1 ? empty : ""));
         aList.push(bundle_session.getString("sm.sessionMenu.previous") + (empty2 ? empty : ""));
      }
      sessionPath.push(gSessionPath[1]);
      sessionPath.push(gSessionPath[2]);
      return {list: aList, path: sessionPath};
   },

   saveAllWindows: function(path, caller, saveClosedTabs) {
      var enumerator = windowEnumerator();
      var wnd, savedTabs = 0 , savedWin = 0, thisWin;
      while ( enumerator.hasMoreElements() ) {
         wnd = enumerator.getNext();
         thisWin = wnd.SessionManager.saveOneWindow(path, caller, false, saveClosedTabs);
         savedTabs += thisWin;
         if (thisWin > 0) savedWin += 1;
      }
      return {win: savedWin, tab: savedTabs};
   },

   checkTime: function SM_checkTime(container, curTime) {
      // check the timestamp for each saved window
      // if the time diff is more than 10sec set dontLoad=true to the saved window
      var windowEnum = container.GetElements();
      while (windowEnum.hasMoreElements()) {
         var rdfNodeLastWindow = windowEnum.getNext();
         var lastSaved = this.getLiteralValue(rdfNodeLastWindow, "timestamp", 0);
         if ((curTime - lastSaved) > 10000) {
            if (!this.nodeHasArc(rdfNodeLastWindow, "dontLoad"))
               this.setLiteral(rdfNodeLastWindow, "dontLoad", "true");
         }
      }
   },

   saveOneWindow: function SM_saveOneWindow(path, caller, overwriteWindow, saveClosedTabs) {
      if (gBrowser.isBlankWindow()) return 0; // dont save window without any tab
      if (!path) path = gSessionPath[0];
      if (!caller) caller = "";
      if (!overwriteWindow) overwriteWindow = false;
      if (typeof(saveClosedTabs) == "undefined") saveClosedTabs = this.saveClosedtabs;
      // if we going to delete close window from the list we can't use GetCount as ID,
      // we need to save unink ID
      var winID;
      if (caller == "windowclosed" || caller == "windowbackup") winID = gBrowser.windowID;
      else winID = this.RDFService.GetAnonymousResource().Value.split("rdf:")[1];
      var winPath = path + "/" + winID;
      this.initSession(path, winPath);
      var savedTabs;
      if (caller == "windowclosed" && this.enableBackup) {
         var tabContainer = this.initContainer(gThisWinTabs);
         // save busy tabs
         this.saveAllTab(winPath, 0, true);
         savedTabs = tabContainer.GetCount() > 0 ? tabContainer.GetCount() : this.saveAllTab(winPath, 0);
         this.setTabsScroll();
      } else {
         savedTabs = this.saveAllTab(winPath, 0);
         if (((gThisWin == winPath && !this.enableBackup) || gThisWin != winPath) && saveClosedTabs)
            this.copyClosedTabsToRDF(winPath);
      }

      var rdfNodeThisWindow = this.RDFService.GetResource(winPath);
      if (SessionPref.getBoolPref("save.selectedtab")) // save selected tab index
         this.setIntLiteral(rdfNodeThisWindow, "selectedIndex", this.getTabPosition());

      if (caller == "windowbackup") {
         this.dataFlush();
         return savedTabs;
      }

      if (path == gSessionPath[0]) {
         // save current tab title. we will use it later in closed windows list as menu entry label
         // if current tab is blank get label from first saved tab that isn't blank
         var label;
         if (gBrowser.isBlankTab(gBrowser.mCurrentTab)) {
            for (var i = 0; i < gBrowser.mTabs.length; i++) {
               var aTab = gBrowser.mTabs[i];
               if (!gBrowser.isBlankTab(aTab)) {
                  label = aTab.label;
                  break;
               }
            }
         } else label = gBrowser.mCurrentTab.label;
         this.setLiteral(rdfNodeThisWindow, "name", label);
         this.setLiteral(rdfNodeThisWindow, "nameExt", this.getNameData(-1, savedTabs));
         var pref = "extensions.tabmix.warnAboutClosingTabs.timeout";
         var delay = gPref.prefHasUserValue(pref) ? gPref.getCharPref(pref)*1 : 0;
         var newTime = new Date().valueOf() - delay;
         this.setLiteral(rdfNodeThisWindow, "timestamp", newTime);
         // if we overwrite window we don't load it again on restart
         if (this.overwriteWindow || overwriteWindow) this.setLiteral(rdfNodeThisWindow, "dontLoad", "true");
         this.setLiteral(rdfNodeThisWindow, "status", "saved");
         this.updateClosedWindowsMenu(false);
      } else this.setLiteral(rdfNodeThisWindow, "status", "");
      this.dataFlush();
      return savedTabs;
   }, // end of "saveOneWindow : function ()"

   // if this session is not in the container add it to the last place and init prop
   // else move it to the last place
   initSession: function SM_initSession(path, winPath) {
      var container = this.initContainer(path);
      var rdfNode = this.RDFService.GetResource(winPath);
      var index = container.IndexOf(rdfNode);
      if (index == -1) {
         container.AppendElement(rdfNode);
         this.setLiteral(rdfNode, "status", "backup");
         this.setResource(rdfNode, "tabs", winPath + "/tabs");
         this.setResource(rdfNode, "closedtabs", winPath + "/closedtabs");
      } else if (index != container.GetCount()) {
         container.RemoveElementAt(index, true);
         container.AppendElement(rdfNode);
      }
   },

   // xxx need to fix this to save only history, image and history index
   // and save the rest when tab added
   tabLoaded: function(aTab) {
      if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
      if (gBrowser.isBlankTab(aTab)) return;
      // if this window is not in the container add it to the last place
      this.initSession(gSessionPath[0], gThisWin);
      var tabContainer = this.initContainer(gThisWinTabs);
      var result = this.saveTab(aTab, gThisWinTabs, tabContainer, true, 0);
      if (result) this.dataFlush();
   },

   updateTabPos: function(aTab, label, add0_1) {
      var tab, node;
      if (!add0_1) add0_1 = 0;
      for (var i = aTab._tPos + add0_1; i < gBrowser.mTabs.length; i++) {
         tab = gBrowser.mTabs[i];
         node = (typeof(label) == "undefined") ? this.getNodeForTab(tab) : label + "/" + tab.linkedPanel;
         this.setIntLiteral(node, "tabPos", tab._tPos);
         this.setIntLiteral(node, "indent", tab.indent);
      }
   },

   tabClosed: function(aTab) { // delete tab from container and save to closed tab list backup
      // we don't check aTab.hasAttribute("inrestore") , in case tab is closed befor
      // its finish to restore.
      if (!this.enableBackup) return;
      this.initSession(gSessionPath[0], gThisWin);
      var tabContainer = this.initContainer(gThisWinTabs);
      var panelPath = this.getNodeForTab(aTab);
      var nodeToClose = this.RDFService.GetResource(panelPath);
      this.updateTabPos(aTab); // update _tPos for the tab right to the deleted tab
      if (this.saveClosedtabs) {
         // move closedtabs to closedtabs container
         var closedTabContainer = this.initContainer(gThisWinClosedtabs);
         var tabExist = true;
         if (tabContainer.IndexOf(nodeToClose) == -1) {
            tabExist = this.saveTab(aTab, gThisWinTabs, closedTabContainer, false, 0);
         } else tabContainer.RemoveElement(nodeToClose, true);
         if (tabExist) {
            closedTabContainer.AppendElement(nodeToClose);
            if (closedTabContainer.GetCount() > tabxPrefs.getIntPref("undoCloseCache"))
               this.deleteClosedtabAt(1, gThisWin);
         }
      } else if (tabContainer.IndexOf(nodeToClose) > -1) {
// xxx  try to use deleteSession ????
         this.deleteSubtree(panelPath);
         tabContainer.RemoveElement(nodeToClose, true);
// xxx is it necessary to delete this ???
         if (!tabContainer.GetCount()) { // if no tab in the container remove it from the tree
            var winContainer = this.initContainer(gSessionPath[0]);
            var rdfNode = this.RDFService.GetResource(gThisWin);
            winContainer.RemoveElement(rdfNode, true);
            this.deleteSubtree(gThisWin);
         }
      }
      this.dataFlush();
   },

   updateTabProp: function SM_updateTabProp(aTab, needFlush) {
      // we dont need this function to run befor sessionmanager init
      // this.enableBackup=null so we dont past the next if
      if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
      if (gBrowser.isBlankTab(aTab)) return; // dont write blank tab to the file
      this.initSession(gSessionPath[0], gThisWin);
      this.setLiteral(this.getNodeForTab(aTab), "properties", SessionData.getTabProperties(aTab, true));

      if (needFlush)
         this.dataFlush();
   },

   tabMoved: function SM_tabMoved(aTab, oldPos, newPos) {
      if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
      this.initSession(gSessionPath[0], gThisWin);
      // can't use aTab._tPos after group of tab delete
      // we pass old position and new position from TMmoveTabTo
      // we need to fix tabPos for all tab between old position and new position
      var first = Math.min(oldPos, newPos);
      var last = Math.max(oldPos, newPos);
      for (var i = first; i < last + 1; i++) {
         var tab = gBrowser.mTabs[i];
         if (!gBrowser.isBlankTab(tab)) {
           this.setIntLiteral(this.getNodeForTab(tab), "tabPos", i);
           this.setIntLiteral(this.getNodeForTab(tab), "indent", gBrowser.mTabs[i].indent);
         }
      }
      this.dataFlush();
   },

   setTabsScroll: function() {
      if (SessionPref.getBoolPref("save.scrollposition"))
         for (var i = 0; i < gBrowser.mTabs.length; i++)
            this.tabScrolled(gBrowser.mTabs[i]);
   },

   // xxx need to find the right event to trigger this function..
   tabScrolled: function SM_tabScrolled(aTab) {
      if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
      var aBrowser = gBrowser.getBrowserForTab(aTab);
      if (gBrowser.isBlankBrowser(aBrowser)) return;
      var bContent = aBrowser.contentWindow;
      var zoomFactor = aBrowser.docShell.contentViewer ? aBrowser.markupDocumentViewer.textZoom : 1;
      this.setLiteral(this.getNodeForTab(aTab), "scroll", bContent.scrollX + "," + bContent.scrollY + "," + zoomFactor);
   },

   tabSelected: function(needFlush) {
      if (!this.enableBackup || gBrowser.mCurrentTab.hasAttribute("inrestore")) return;
      if (typeof(needFlush) == "undefined") needFlush = false;
      this.initSession(gSessionPath[0], gThisWin);
      this.setTabsScroll(); // until i find proper event to update tab scroll do it from here
      if (SessionPref.getBoolPref("save.selectedtab")) {
         this.setIntLiteral(gThisWin, "selectedIndex", this.getTabPosition());
      }
      if (needFlush) this.dataFlush();
   },

   getTabPosition: function() { // calc selected tab position if blank tab not restore
      if (gBrowser.isBlankTab(gBrowser.mCurrentTab)) return 0; // if the current tab is blank we don't resore the index
      var blankTab = 0;
      for (var i = 0; i < gBrowser.mCurrentTab._tPos; i++) {
         if (gBrowser.isBlankTab(gBrowser.mTabs[i])) blankTab++;
      }
      return gBrowser.mCurrentTab._tPos - blankTab;
   },

   getNodeForTab: function(aTab) {
      return gThisWinTabs + "/" + aTab.linkedPanel;
   },

   saveAllTab: function(winPath, offset, saveBusy) {
      var savedTabs = 0 ;
      var rdfNodeTabs = this.getResource(winPath, "tabs");
      var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
      var tabContainer = this.initContainer(rdfNodeTabs);
      for (var i = 0; i < gBrowser.mTabs.length; i++) {
         var aTab = gBrowser.mTabs[i];
         if (saveBusy && !aTab.hasAttribute("busy")) continue; // save only busy tabs
         if (this.saveTab(aTab, rdfLabelTabs, tabContainer, true, offset)) savedTabs ++;
      }
      return savedTabs;
   },

   // call from tabloaded, tabClosed, saveAllTab
// xxx add flag what to save : all, history, property, scrollPosition
   saveTab: function(aTab, rdfLabelTabs, tabContainer, needToAppend, offset) {
      var aBrowser = gBrowser.getBrowserForTab(aTab);
      if (gBrowser.isBlankBrowser(aBrowser)) return false;

      var sessionHistory = aBrowser.webNavigation.sessionHistory;
      var rdfLabelTab = rdfLabelTabs + "/" + aTab.linkedPanel;
      var index = sessionHistory.index < 0 ? 0 : sessionHistory.index;
      var historyIndex = this.enableSaveHistory ? index : 0;
      var rdfNodeTab = this.RDFService.GetResource(rdfLabelTab);

      var bContent = aBrowser.contentWindow;
      var zoomFactor = aBrowser.docShell.contentViewer ? aBrowser.markupDocumentViewer.textZoom : 1;
      var scrollPos = bContent.scrollX + "," + bContent.scrollY + "," + zoomFactor;
      try {
         var curHistory = sessionHistory.getEntryAtIndex(index, false);
         curHistory.QueryInterface(tmSHEntry).setScrollPosition(bContent.scrollX, bContent.scrollY);
      } catch (e) {tmLog("saveTab error index " + sessionHistory.index + "\n" + e); }

      this.saveTabData(rdfNodeTab, historyIndex, aTab._tPos + offset,
               aTab.indent,
               aTab.getAttribute("image"),
               SessionData.getTabProperties(aTab, true),
               this.saveTabHistory(sessionHistory),
               scrollPos);
      // dont append if we call from tabClosed function
//XXX move this to the caller function
      if (tabContainer.IndexOf(rdfNodeTab) == -1 && needToAppend) {
         tabContainer.AppendElement(rdfNodeTab);
         this.updateTabPos(aTab, rdfLabelTabs, 1); // update _tPos for the tab right to the new tab
      }
      return true;
   },

   saveTabData: function SM_saveTabData(node, index, tabPos, indent, image, properties, history, scroll) {
      this.setIntLiteral(node, "index",      index);
      this.setIntLiteral(node, "tabPos",     tabPos);
      this.setIntLiteral(node, "indent",     indent);
      this.setLiteral   (node, "image",      image || ""); // for use in closed tab list
      this.setLiteral   (node, "properties", properties);
      this.setLiteral   (node, "history",    history);
      this.setLiteral   (node, "scroll",     scroll);
   },

   // xxx save text size (zoom), char type ?
   saveTabHistory: function(sessionHistory) {
      var historyStart = this.enableSaveHistory ? 0 : sessionHistory.index;
      var historyEnd = this.enableSaveHistory ? sessionHistory.count : sessionHistory.index+1;
      var j, historyEntry, history = [];
      for (j = historyStart; j < historyEnd; j++) {
         try {
            historyEntry = sessionHistory.getEntryAtIndex(j, false).QueryInterface(tmSHEntry);
            history.push(historyEntry.title);
            history.push(historyEntry.URI.spec);
            history.push(this.getScrollPosHs(historyEntry)); // not in use yet
         } catch (e) {tmLog("saveTabHistory error index " + j + "\n" + e); }
      }
      // generate unique separator and combine the array to one string
      var separator = "][", extraSeparator = "@";
      for (var i = 0; i < history.length; ++i) {
         while (history[i].indexOf(separator) > -1)
            separator += extraSeparator;
      }
      // insert the separator to history so we can extract it in loadTabHistory
      return separator + "|-|" + history.join(separator);
   },

   getScrollPosHs: function(historyEntry) {
      if (SessionPref.getBoolPref("save.scrollposition")) {
         var x={}, y={};
         historyEntry.getScrollPosition(x, y);
         return x.value + "," + y.value;
      } return "0,0";
   },

   loadSession: function(path, caller, overwriteWindows) {
      var sessionContainer = this.initContainer(path);
      var sessionEnum = sessionContainer.GetElements();
      var sessionCount = 0, concatenate;
      var windowEnum = windowEnumerator();
      if (typeof(overwriteWindows) == "undefined")
         overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows");
      // don't concatenate window after crash
      if (caller == "firstwindowopen" && this.getLiteralValue(gSessionPath[0], "status") == "crash2")
         concatenate = false;
      else
         concatenate = SessionPref.getBoolPref("restore.concatenate");
      var saveBeforOverwrite = SessionPref.getBoolPref("restore.saveoverwrite");
      var overwriteTabs = SessionPref.getBoolPref("restore.overwritetabs");

      // in single window mode we restore ALL window into this window
      if (gSingleWindowMode)
         concatenate = true;

      // if this window is blank use it when reload session
      var wnd, blankWindow, id;
      if (!gSingleWindowMode && concatenate && !overwriteWindows && !gBrowser.isBlankWindow() && caller != "firstwindowopen" && caller != "concatenatewindows") {
         id = this.RDFService.GetAnonymousResource().Value.split("rdf:")[1];
         window.openDialog( getBrowserURL(), "tabmix-"+path+"-concatenatewindows-"+id, "chrome,all,dialog=no", null);
         return;
      }
      // if we join all window to one window
      // call the same window for all saved window with overwritewindows=false and overwritetabs=false if this not the first saved
      // for first saved window overwritetabs determined by user pref
      while (sessionEnum.hasMoreElements()) {
         sessionCount++;
         var rdfNodeSession = sessionEnum.getNext();
         if (rdfNodeSession instanceof tmRDFResource) {
            var windowPath = rdfNodeSession.QueryInterface(tmRDFResource).Value;
            if (this.nodeHasArc(windowPath, "dontLoad")) continue;
            if (concatenate) {
               if (caller != "concatenatewindows" && caller != "firstwindowopen" && sessionCount == 1
                  && saveBeforOverwrite && overwriteTabs) this.saveOneWindow(gSessionPath[0], "", true);
               var newCaller = (sessionCount != 1) ? caller+"-concatenate" : caller;
               this.loadOneWindow(windowPath, newCaller);
            } else {
               wnd = null;
               blankWindow = false;
               if (windowEnum.hasMoreElements()) {
                  wnd = windowEnum.getNext();
                  blankWindow = wnd.gBrowser.isBlankWindow();
               }
               if (wnd != null && (overwriteWindows || blankWindow || (caller == "firstwindowopen" && sessionCount == 1 ))) {
                  // if we save overwrite windows in the closed windows list don't forget to set dontLoad==true
                  if (caller != "firstwindowopen" && saveBeforOverwrite && overwriteTabs)
                     wnd.SessionManager.saveOneWindow(gSessionPath[0], "", true);
                  wnd.SessionManager.loadOneWindow(windowPath, caller);
               } else {
                  // xxx need to use window.arguments not the name, maybe to pass the tab list as window.arguments[0]
                  // so they open at startup and than to add history ???????
                  id = this.RDFService.GetAnonymousResource().Value.split("rdf:")[1];
                  window.openDialog( getBrowserURL(), "tabmix-"+windowPath+"-"+caller+"-"+id, "chrome,all,dialog=no", null);
               }
            }
         }
      }
      // cloes extra windows if we overwrite open windows and set dontLoad==true
      if (numberOfWindows() > 1 && overwriteWindows) {
         while (windowEnum.hasMoreElements()) {
            wnd = windowEnum.getNext();
            if (concatenate && wnd == window) continue;
            if (saveBeforOverwrite) wnd.SessionManager.overwriteWindow = true;
            else wnd.SessionManager.saveThisWindow = false;
            wnd.close();
         }
      }
   },

   openclosedwindow: function SM_openclosedwindow(path, overwriteWindows) {
      // 1. check if to overwrite the opener window
      //    if 1 is true call loadOneWindow
      //    if 1 is false open new window and pass the path
      // 2. delete the window from closedwindow list (after new window is opend and load)
      var rdfNodeClosedWindow = this.RDFService.GetResource(path);
      // don't reopen same window again. the window removed from closed window list after it finish to load
      if (this.nodeHasArc(rdfNodeClosedWindow, "reOpened")) return;
      this.setLiteral(rdfNodeClosedWindow, "reOpened", "true");
      if (typeof(overwriteWindows) == "undefined") overwriteWindows = SessionPref.getBoolPref("restore.overwritewindows");
      var saveBeforOverwrite = SessionPref.getBoolPref("restore.saveoverwrite");
      var overwriteTabs = SessionPref.getBoolPref("restore.overwritetabs");
      if (overwriteWindows || gBrowser.isBlankWindow() || gSingleWindowMode) {
         if (saveBeforOverwrite && overwriteTabs) this.saveOneWindow(gSessionPath[0], "", true);
         this.loadOneWindow(path, "openclosedwindow");
      } else {
         var id = this.RDFService.GetAnonymousResource().Value.split("rdf:")[1];
         window.openDialog( getBrowserURL(), "tabmix-"+path+"-openclosedwindow-"+id, "chrome,all,dialog=no", null);
      }
      this.dataFlush();
   },

   loadOneWindow: function SM_loadOneWindow(path, caller) {
      var overwrite = true, restoreSelect = SessionPref.getBoolPref("save.selectedtab");
      switch ( caller ) {
         case "firstwindowopen":
               if (window.arguments && window.arguments.length > 0) {
                  overwrite = window.arguments[0] == gHomeButton.getHomePage() ? true : false ;
                  if (!overwrite && window.arguments[0] != "about:blank") restoreSelect = false;
               } else overwrite = false;
            break;
         case "windowopenebytabmix":
         case "concatenatewindows":
               overwrite = true;
            break;
         case "openclosedwindow":
         case "sessionrestore":
            overwrite = SessionPref.getBoolPref("restore.overwritetabs");
            break;
         case "firstwindowopen-concatenate":
         case "openclosedwindow-concatenate":
         case "sessionrestore-concatenate":
         case "concatenatewindows-concatenate":
            overwrite = false;
            break;
         default: tmLog("SessionManager \n error unidentifid caller " + caller);
      }
/*
      1. when open first windows overwrite tab only if they are home page, if firefox open from link or with
         pages that are not the home page append the new tab to the end.
         simple solution is to set browser.startup.page = 0 , when we activate session manager, in this case if we
         have any tabs in the first window we don't overwrite.
      2. when open window by session manager other than the first window (caller = "windowopenebytabmix" and tabmix in the name) overwrite=true
      3. when loadOneWindow call by openclosedwindow or loadSession we reuse window check user pref for overwrite.
      4. if we open all closed windows to one window append tab to the end and select the selected tab from first window
         in the session.
*/
      var cTab = gBrowser.mCurrentTab;
      var concatenate = caller.indexOf("-concatenate") != -1 || (caller == "firstwindowopen" && gBrowser.mTabContainer.childNodes.length > 1);
      var rdfNodeWindow = this.RDFService.GetResource(path);

      var rdfNodeTabs = this.getResource(rdfNodeWindow, "tabs");
      if (!(rdfNodeTabs instanceof tmRDFResource) || this.containerEmpty(rdfNodeTabs)) {
         var bundle_session = document.getElementById("bundle_session_manager");
         alert(bundle_session.getString("sm.restoreError.msg0") + "\n"  + bundle_session.getString("sm.restoreError.msg1"));
         var stringBundle = document.getElementById("tmp-string-bundle");
         var tabmix_loading = stringBundle.getString("session.loading.label") + "...";
         if (gBrowser.mCurrentTab.label == tabmix_loading)
            gBrowser.mCurrentBrowser.reload();
         return;
      }
      var tabContainer = this.initContainer(rdfNodeTabs);
      var newtabsCount = tabContainer.GetCount();
      gBrowser.tabsToLoad = newtabsCount;
      this.setStripVisibility(newtabsCount);
      var lastSelectedIndex = restoreSelect ? this.getIntValue(rdfNodeWindow, "selectedIndex") : 0;
      if (lastSelectedIndex < 0 || lastSelectedIndex >= newtabsCount) lastSelectedIndex = 0;
      var i, newIndex, aTab, tabPos;
      if (overwrite) {
         gBrowser.mTabContainer.collapsedTabs = 0;
         for ( i = 0; i < gBrowser.mTabContainer.childNodes.length; i++) {
            aTab = gBrowser.mTabContainer.childNodes[i];
            var aBrowser = gBrowser.getBrowserForTab(aTab);
            // reset old history
            aBrowser.webNavigation.sessionHistory =
               Components.classes["@mozilla.org/browser/shistory;1"]
               .createInstance(tmSHistory);
            // reset old text zoom
            setTextZoom(aBrowser, 1);
            // remove selected and flst_id from all tabs but the current
            if (aTab != cTab) {
               aTab.removeAttribute("selected");
               aTab.removeAttribute("flst_id");
            }
         }
         while (newtabsCount > gBrowser.mTabContainer.childNodes.length) {
            gBrowser.addTab();
         }
         // move selected tab to place
         gBrowser.TMmoveTabTo(cTab, lastSelectedIndex);
         // remove extra tab
         while (newtabsCount < gBrowser.mTabContainer.childNodes.length) {
            gBrowser.removeTab(gBrowser.mTabContainer.lastChild);
         }
         newIndex = 0;
      } else {
         // reuse blank tabs and move tabs to the right place
         var openTabNext = gPref.getBoolPref("extensions.tabmix.openTabNext") &&
           !gPref.getBoolPref("extensions.tabmix.treemode");

         // catch blank tab for reuse
         var blankTabs = [], blankTabsCount = 0, currentTabIsBalnk = false;
         for (i = 0; i < gBrowser.mTabContainer.childNodes.length ; i++) {
            aTab = gBrowser.mTabContainer.childNodes[i];
            if (!aTab.loadOnStartup) { // make sure we not overwrite tab that loads from apps
               if (gBrowser.isBlankTab(aTab) && (aTab.hasAttribute("tabmix_busy") || !aTab.hasAttribute("busy"))) {
                  aTab.removeAttribute("tabmix_busy");
                  if (aTab != cTab) {
                     blankTabs.push(aTab);
                     aTab.removeAttribute("selected");
                     aTab.removeAttribute("flst_id");
                  } else {
                     blankTabs.unshift(aTab);
                     currentTabIsBalnk = true;
                  }
               }
            } else delete aTab.loadOnStartup;
         }
         // remove extra tabs
         var blankTab;
         while (blankTabs.length > newtabsCount) {
            blankTab = blankTabs.pop();
            if (blankTab) gBrowser.removeTab(blankTab);
         }
         var newPos = (openTabNext && cTab._tPos < gBrowser.mTabContainer.childNodes.length - 1 && !concatenate) ? cTab._tPos + 1 : gBrowser.mTabContainer.childNodes.length - 1;
         // move blank tabs to new position
         blankTabsCount = blankTabs.length;
         while (blankTabs.length > 0) {
            blankTab = blankTabs.shift();
            tabPos = (blankTab._tPos < newPos && newPos < gBrowser.mTabContainer.childNodes.length - 1) ? newPos - 1 : newPos;
            gBrowser.TMmoveTabTo(blankTab, tabPos);
         }
         var tabsCount = gBrowser.mTabContainer.childNodes.length;
         var newTotalTabsCount = tabsCount - blankTabsCount + newtabsCount;
         while (newTotalTabsCount > gBrowser.mTabContainer.childNodes.length) {
            var newTab = gBrowser.addTab();
            // in concatenate mode move tab to the end of the list
            if (concatenate && openTabNext) {
               gBrowser.TMmoveTabTo(newTab, gBrowser.mTabContainer.childNodes.length-1);
            }
         }
         if (tabsCount == blankTabsCount) newPos = 0;
         else newPos = (openTabNext && cTab._tPos < gBrowser.mTabContainer.childNodes.length - 1 && !concatenate) ? cTab._tPos + 1 : tabsCount - blankTabsCount;
         if (!concatenate && restoreSelect) { // in concatenate mode we select tab only from first window
            if (currentTabIsBalnk) { // if the current tab is not blank select new tab
               if (openTabNext && newPos > 0) newPos--;
               // move selected tab to place
               gBrowser.TMmoveTabTo(cTab, newPos + lastSelectedIndex);
            } else {
               // Set newly selected tab after quick timeout, like in firefox code
               function TM_selectNewTab(browser, tabs, newIndex, oldIndex, updateSelected) {
                  browser.selectedTab = tabs[newIndex];
                  if (updateSelected) {
                     tabs[oldIndex].removeAttribute("selected");
                     tabs[oldIndex].removeAttribute("flst_id");
                  }
               }
               var cIndex = gBrowser.mTabContainer.selectedIndex;
               if ((newPos + lastSelectedIndex) != cIndex) {
                  var updateSelected = (caller=="firstwindowopen" || caller=="windowopenebytabmix") ;
                  setTimeout(TM_selectNewTab, 0, gBrowser, gBrowser.mTabContainer.childNodes, newPos + lastSelectedIndex, cIndex, updateSelected);
               }
            }
         }
         newIndex = newPos;
      }
      // call ensureTabIsVisible befor and after we reload the tab
      gBrowser.mTabContainer.ensureTabIsVisible(gBrowser.mTabContainer.selectedIndex);
      var tabsEnum = tabContainer.GetElements();
      // sort the tab by "tabPos"
      var rdfTabs = [], rdfNodeTab;
      while (tabsEnum.hasMoreElements()) {
         rdfNodeTab = tabsEnum.getNext();
         if (rdfNodeTab instanceof tmRDFResource) {
            tabPos = this.getIntValue(rdfNodeTab, "tabPos");
            rdfTabs.push([tabPos, rdfNodeTab]);
         }
      }
      rdfTabs.sort(this.sortByColumn(0 ,true));
      this.initSession(gSessionPath[0], gThisWin); // init the new container befor we start to load data

      // restore the selected tab first
      var selectedTabLoaded;
      if (restoreSelect && lastSelectedIndex in rdfTabs) {
         selectedTabLoaded = true;
         aTab = gBrowser.mTabContainer.childNodes[newIndex + lastSelectedIndex];
         this.loadOneTab(rdfTabs[lastSelectedIndex][1], aTab);
      }

      for (i = 0; i < rdfTabs.length ; i++) {
         if (selectedTabLoaded && lastSelectedIndex == i)
            continue;
         aTab = gBrowser.mTabContainer.childNodes[newIndex + i];
         this.loadOneTab(rdfTabs[i][1], aTab);
      }

      if (this.saveClosedtabs)
         this.saveClosedTabs(path, gThisWin, "closedtabs", true);  // load prev saved closed tabs and save to current backup
      gBrowser.mTabContainer.nextTab = 1;
      // if we open closed window delete this window from closed window list
      var caller1;
      if (window.name.indexOf("tabmix") != -1) caller1 = window.name.split("-")[2];
      if (caller == "openclosedwindow" || caller1 == "openclosedwindow"){
         if (this.nodeHasArc(rdfNodeWindow, "reOpened")) {
            this.removeSession(path, gSessionPath[0]);
            this.updateClosedWindowsMenu("check");
         }
      }
   },

   setStripVisibility: function(tabCount) {
      if (tabCount > 1 && gPref.getBoolPref("browser.tabs.autoHide") && gBrowser.mStrip.collapsed) {
        // unhide the tab bar
        gBrowser.setStripVisibilityTo(true);
        gPref.setBoolPref("browser.tabs.forceHide", false);
      }
   },

   sortByColumn: function(nCol, bDescending) {
      var c = nCol;
      var d = bDescending;
      return function (n1, n2) {
         if (n1[c] < n2[c]) return (d) ? -1 : +1;
         if (n1[c] > n2[c]) return (d) ? +1 : -1;
         return 0;
      }
   },

   saveClosedTabs: function(fromPath, toPath, conPath, updateClosedTabsList) {
      var isClosedTabs = conPath == "closedtabs";
      if (isClosedTabs && !(this.saveClosedtabs))
         return;

      var fromOld = this.wrapContainer(fromPath, conPath);
      if (!(fromOld.Root instanceof tmRDFResource)) return;
      var toNew = this.wrapContainer(toPath, conPath);
      var rdfNodeTabs = this.getResource(toPath, "tabs");
      var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
      var newIndex = -1;
      while (fromOld.Enum.hasMoreElements()) {
         var rdfNodeSession = fromOld.Enum.getNext();
         if (!(rdfNodeSession instanceof tmRDFResource)) continue;
         newIndex++;
         if (isClosedTabs && (fromOld.Count - newIndex > gPref.getIntPref("extensions.tabmix.undoCloseCache"))) continue;
         var uniqueId = "panel" + Date.now() + newIndex;
         var rdfLabelSession = rdfLabelTabs + "/" + uniqueId;
         var newNode = this.RDFService.GetResource(rdfLabelSession);
         var tabPos = this.getIntValue(rdfNodeSession, "tabPos");
         var image = this.getLiteralValue(rdfNodeSession, "image");
         var prop = this.getLiteralValue(rdfNodeSession, "properties");
         var history = this.getLiteralValue(rdfNodeSession, "history");
         var index = this.getIntValue(rdfNodeSession, "index");
         var scrollPos = this.getLiteralValue(rdfNodeSession, "scroll"); // including zoom factor
         var maxCache = gPref.getIntPref("extensions.tabmix.undoCloseCache");
         if (this.enableBackup) { // save only if backup enabled
            toNew.Container.AppendElement(newNode);
            this.saveTabData(newNode, index, tabPos, 0, image, prop, history, scrollPos);
            // delete old entry if closedTabs container wasn't empty
            if (isClosedTabs && (toNew.Container.GetCount() > maxCache))
               this.deleteClosedtabAt(1, toPath);
         }
         if(updateClosedTabsList) {
            var savedHistory = this.loadTabHistory(rdfNodeSession);
            if (savedHistory == null) {
               tmLog("closed tab at index " + newIndex + " failed to load data from the saved session");
               continue;
            }
            try {
               var title = savedHistory.history.getEntryAtIndex(savedHistory.index, true).title;
               if (!SessionPref.getBoolPref("save.scrollposition"))
                  scrollPos = "0,0,1";
               else if (scrollPos.split(",").length < 3) // version befor 0.3.0.603 don't include zoomfactor
                     scrollPos += ",1"
               gBrowser.closedTabs.push([tabPos, savedHistory.history, title, image, prop, scrollPos]);
            } catch (e) {tmLog("saveClosedTabs error index " + savedHistory.index + "\n" + e); }
            // delete old entry if gBrowser.closedTabs wasn't empty
            if (gBrowser.closedTabs.length > maxCache)
               gBrowser.closedTabs.shift();
         }
      }
      if (updateClosedTabsList && gBrowser.closedTabs.length > 0 && document.getElementById("btn_undoclose"))
         document.getElementById("btn_undoclose").setAttribute("disabled", "false");
   },

   copyClosedTabsToRDF: function(winPath) {
      var rdfNodeTo = this.getResource(winPath, "closedtabs");
      var toContainer = this.initContainer(rdfNodeTo);
      var rdfNodeTabs = this.getResource(winPath, "tabs");
      var rdfLabelTabs = rdfNodeTabs.QueryInterface(tmRDFResource).Value;
      var ctabs = gBrowser.closedTabs;
      var tabCount = ctabs.length;
      var maxCache = gPref.getIntPref("extensions.tabmix.undoCloseCache");
      var aTab, uniqueId, rdfLabelSession, newNode, historyEntry, scrollPos, history;
      for (var i = 0; i < tabCount; i++) {
         aTab = ctabs[i];
         if (tabCount - i <= maxCache) {
            uniqueId = "panel" + Date.now() + i;
            rdfLabelSession = rdfLabelTabs + "/" + uniqueId;
            newNode = this.RDFService.GetResource(rdfLabelSession);
            toContainer.AppendElement(newNode);
            history = this.saveTabHistory(aTab[1])
            scrollPos = SessionPref.getBoolPref("save.scrollposition") ? aTab[5] : "0,0,1";
            this.saveTabData(newNode, aTab[1].index, aTab[0], 0, aTab[3], aTab[4], history, scrollPos);
            // delete old entry if closedTabs container wasn't empty
            if (toContainer.GetCount() > maxCache)
               this.deleteClosedtabAt(1, winPath);
         }
      }
      this.dataFlush();
   },

   fixClosedtabTabPos: function(aPos) {
      if (!(this.saveClosedtabs))
         return;

      var closedTabs = this.wrapContainer(gThisWin, "closedtabs");
      while (closedTabs.Enum.hasMoreElements()) {
         var node = closedTabs.Enum.getNext();
         var tabPos = this.getIntValue(node, "tabPos");
         if (tabPos >= aPos) this.setIntLiteral(node, "tabPos", tabPos++);
      }
      this.dataFlush();
   },

   deleteAllClosedtabs: function(sessionContainer) { // delete all closed tabs in this session
      var windowEnum = sessionContainer.GetElements();
      while (windowEnum.hasMoreElements()) {
         var rdfNodeWindow = windowEnum.getNext();
         this.deleteWinClosedtabs(rdfNodeWindow.QueryInterface(tmRDFResource).Value);
      }
   },

   deleteWinClosedtabs: function(winPath) {
      var rdfNodeTabs = this.getResource(winPath, "closedtabs");
      var container = this.initContainer(rdfNodeTabs);
      this.deleteWithProp(container);
      this.dataFlush();
   },

   deleteClosedtabAt: function(index, winPath) {
      if (!SessionPref.getBoolPref("save.closedtabs"))
         return;

      if (typeof(winPath) == 'undefined') winPath = gThisWin;
      var rdfNodeTabs = this.getResource(winPath, "closedtabs");
      var container = this.initContainer(rdfNodeTabs);
      if (index == "last") index = container.GetCount();
      if (index < 1 || index > container.GetCount()) return;
      var nodeToDelete = container.RemoveElementAt(index, true);
      var nodeValue = nodeToDelete.QueryInterface(tmRDFResource).Value
      this.deleteSubtree(nodeValue);
      if (!container.GetCount()) this.deleteNode(rdfNodeTabs);
      this.dataFlush();
   },

   loadOneTab: function SM_loadOneTab(rdfNodeSession, aTab) {
      aTab.setAttribute("inrestore", "true"); // flag. dont save tab that are in restore phase
      // load Properties befor we load History
      var tabProperties = this.getLiteralValue(rdfNodeSession, "properties");
      if (tabProperties != "") SessionData.setTabProperties(aTab, tabProperties, true);
      aTab.indent = this.getIntValue(rdfNodeSession, "indent");
      var aBrowser = gBrowser.getBrowserForTab(aTab);
      var webNav = aBrowser.webNavigation;
      var savedHistory = this.loadTabHistory(rdfNodeSession, webNav.sessionHistory);
      if (savedHistory == null) {
         tmLog("loadOneTab() - tab at index " + aTab._tPos + " failed to load data from the saved session");
         gBrowser.removeTab(aTab);
         return;
      }

      aBrowser._sessionData = {
         tabPos: aTab._tPos,
         node: rdfNodeSession
      };

      try {
         // if url is file and file don't exist it throws an exception, we don't need to reload loacl file
         var url = webNav.sessionHistory.getEntryAtIndex(savedHistory.index, false).URI.spec;
         var needToReload = SessionPref.getBoolPref("restore.reloadall") && url.indexOf("file:")!=0;
         if (needToReload) {
            window.setTimeout( function (browser) {
              browser.addEventListener('load', SessionManager.afterTabLoad, true);
              const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
              var _webNav = browser.webNavigation;
              try {
                 var sh = _webNav.sessionHistory;
                 if (sh)
                     _webNav = sh.QueryInterface(nsIWebNavigation);
              } catch (e) { }

              try {
                 const flags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
                 _webNav.reload(flags);
              } catch (e) { }
            }, 0, aBrowser);
         }
         else
            aBrowser.addEventListener('load', SessionManager.afterTabLoad, true);

         webNav.gotoIndex(savedHistory.index);
      } catch (e) {tmLog("error in loadOneTab gotoIndex ? ")}

   }, // end of "loadOneTab : function(...............)"

   afterTabLoad: function SM_afterTabLoad(event) {
      var aBrowser = this;
      var data = aBrowser._sessionData;
      // restore scroll position
      if (SessionPref.getBoolPref("save.scrollposition")) {
         var XYZ = SessionManager.getLiteralValue(data.node, "scroll", "0,0,1");
         if (XYZ != "0,0,1") {
            XYZ = XYZ.split(",");
            try {
               var sHistory = aBrowser.webNavigation.sessionHistory;
               var curHistory = sHistory.getEntryAtIndex(sHistory.index, false);
               curHistory.QueryInterface(tmSHEntry).setScrollPosition(XYZ[0], XYZ[1]);
            } catch (e) {tmLog("loadOneTab error index " + sHistory.index + "\n" + e); }
            var zoomFactor = XYZ.length == 3 ? XYZ[2] : 1;
            SessionManager.setScrollPosition(data.tabPos, XYZ[0], XYZ[1], 15, null, zoomFactor);
         }
      }
      gBrowser.mTabs[data.tabPos].removeAttribute("inrestore");
      aBrowser.removeEventListener('load', SessionManager.afterTabLoad, true);
      delete aBrowser._sessionData;


      // call ensureTabIsVisible for the current tab
      gBrowser.mTabContainer.ensureTabIsVisible(gBrowser.mTabContainer.selectedIndex);

      if (aBrowser == gBrowser.mCurrentBrowser && !gBrowser.isBlankBrowser(aBrowser))
         window.content.focus();

      // check if we restore all tabs
      if (--gBrowser.tabsToLoad == 0) {
         delete gBrowser.tabsToLoad;
         checkBeforeAndAfter(); // just in case (we do it also in setTabTitle
         if (SessionManager.enableBackup){
            var result = SessionManager.saveOneWindow(gSessionPath[0], "windowbackup");
            if (result > 0) SessionManager.dataFlush();
         }
         SessionManager.setLiteral(gRDFRoot + "/closedSession/thisSession", "status", "crash");
      }
   },

   setScrollPosition: function SM_setScrollPosition(tabPos, x, y, attempts, href, zoom) {
      // xxx only work for the current index in history.
      // need to find way to make it work for all pages in history
      var tab = gBrowser.mTabs[tabPos];
      var aBrowser = gBrowser.getBrowserForTab(tab);
      var bContent = aBrowser.contentWindow;
      var docViewer;
      if (aBrowser.docShell.contentViewer)
         docViewer = aBrowser.markupDocumentViewer;
      if (!tab.hasAttribute("busy")) {
         if (bContent.scrollX != x || bContent.scrollY != y)
            bContent.scrollTo(x, y);
         if (zoom && docViewer && docViewer.textZoom != zoom)
            docViewer.textZoom = zoom;
      }
      if (attempts && ( bContent.scrollX != x || bContent.scrollY != y || (zoom && docViewer && docViewer.textZoom != zoom) )) {
         window.setTimeout(SessionManager.setScrollPosition, 25, tabPos, x, y, --attempts, href, zoom);
         return;
      } else {
         // if we save this befor timeout sometimes scroll is not ready yet
         if (SessionManager.enableBackup)
            SessionManager.setLiteral(SessionManager.getNodeForTab(tab), "scroll", x + "," + y + "," + zoom);
         // call by openLinkWithHistory
         if (href)
            aBrowser.loadURI(href);
      }
   },

   loadTabHistory: function(rdfNodeSession, sHistoryInternal) {
      var history = this.getLiteralValue(rdfNodeSession, "history");
      var tmpData = history.split("|-|");
      var sep = tmpData.shift(); // remove seperator from data
      var historyData = tmpData.join("|-|").split(sep);
      if (historyData.length < HSitems) {
         tmLog("error in loadTabHistory" + "\n" + "historyData.length " + historyData.length + "\n" + "historyData " + historyData + "\n" + "history " + history);
         return null; // if it les then 3 no data !!
      }
      if (typeof(sHistoryInternal) == "undefined")
         sHistoryInternal = Components.classes["@mozilla.org/browser/shistory;1"]
                                 .createInstance(tmSHistory);
      sHistoryInternal = sHistoryInternal.QueryInterface(Components.interfaces.nsISHistoryInternal);
      var sessionIndex = this.getIntValue(rdfNodeSession, "index");
      var historyCount = historyData.length/HSitems;
      if ( sessionIndex < 0 || sessionIndex >= historyCount ) sessionIndex = historyCount - 1;
      var index, historyEntry, entryTitle, uriStr, newURI, XY;
      for ( var i = 0; i < historyCount; i++ ){
         index = i * HSitems;
         if (!this.enableSaveHistory && sessionIndex != i) continue;
         historyEntry = Components.classes["@mozilla.org/browser/session-history-entry;1"]
                           .createInstance(tmSHEntry);
         entryTitle = historyData[index];
         uriStr = historyData[index + 1];
         if (uriStr == "") uriStr = "about:blank";
         newURI = this.IOService.newURI(uriStr, null, null);
         ("setTitle" in historyEntry) ? historyEntry.setTitle(entryTitle) : historyEntry.SetTitle(entryTitle);
         ("setURI" in historyEntry) ? historyEntry.setURI(newURI) : historyEntry.SetURI(newURI);
         historyEntry.saveLayoutStateFlag = true;
// xxx check if we can use this to reload tab not from Cache;
// is it good to reload ALL the history ???
/*
var needToReload = SessionPref.getBoolPref("restore.reloadall") && uriStr.indexOf("file:")!=0;
historyEntry.loadType = needToReload ? Components.interfaces.nsIDocShellLoadInfo.loadReloadBypassCache // http://lxr.mozilla.org/seamonkey/source/docshell/base/nsIDocShellLoadInfo.idl#73
   : Components.interfaces.nsIDocShellLoadInfo.loadHistory;
*/
         if (SessionPref.getBoolPref("save.scrollposition")) {
            if (historyData[index + 2] != "0,0") {
               XY = historyData[index + 2].split(",");
               historyEntry.setScrollPosition(XY[0], XY[1]); // XY is array [x,y]
            }
         }
         sHistoryInternal.addEntry(historyEntry, true);
      }
      if (!this.enableSaveHistory) sessionIndex = 0;
      return {history: sHistoryInternal, index: sessionIndex};
   }

};
